Я довольно новичок в многопоточности в iOS, и у меня есть некоторые трудности с реализацией моей задачи.Я был бы очень признателен за помощь более опытных программистов.
У меня есть один URL-адрес в качестве отправной точки, к которому я делаю запрос, проверяю некоторую информацию и извлекаю все URL-адреса, содержащиеся в этой сети-page.После этого мне нужно делать то же самое с новыми URL-адресами снова и снова, пока не будет проверено определенное количество URL-адресов.
Я решил использовать Операции и OperationQueues , потому что мне также нужно иметь возможность выбрать максимальное количество одновременных операций.
Я создал пользовательскую асинхронную операцию LoadOperation
и применил ее к каждому URL-адресу, хранящемуся в массиве.В блоке завершения я добавляю новые URL-адреса в массив и вызываю ту же функцию, рекурсивно.
Также я создал две очереди:
- Параллельная для запросов,
и
- Последовательный для доступа к общим свойствам следующим образом:
class Queue<Item: Equatable> {
// concurrent queue for URL-requests
let loadOperationQueue = OperationQueue() // qos: .utilities
// serial operation queue for accessing shared properties
let serialOperationQueue = OperationQueue() // qos: .utilities
// maximum number of URLs that need to be checked ultimately
var maxNumberOfItems: Int = 1
var pendingItems: [Item] = [] // newly added URLs
var loadingItems: [Item] = [] // loading URLs
var processedItems: [Item] = [] // URLs that were checked
// all URLs for tableView dataSource
var allItems: [Item] {
return processedItems + loadingItems + pendingItems
}
func addToPending(_ item: Item) {
guard allItems.count < maxNumberOfItems else {return}
pendingItems.append(item)
}
func removeFromPending() -> Item? {
guard !self.pendingItems.isEmpty else { return nil }
let item = pendingItems.removeFirst()
loadingItems.append(item)
return item
}
func addToProcessed(_ item: Item) {
loadingItems = loadingItems.filter{$0 != item}
processedItems.append(item)
}
func isFull() -> Bool {
return allItems.count >= maxNumberOfItems
}
}
Это моя функция поиска:
func startSearching() {
// iterate over array of URLs creating async operation
while let currentWebPage = queue.removeFromPending() {
let loadOperation = LoadOperation(currentWebPage, searchString: searchString)
loadOperation.completionBlock = {
let webPage = loadOperation.output!
self.queue.serialOperationQueue.addOperation {
// add checked URL to processed
self.queue.addToProcessed(webPage)
// add new urls to an array of pending URLs
for urlString in webPage.containedLinks {
//check if the limit of checked urls is not exceeded
guard !self.queue.isFull() else { return }
//check if this url is already in queue
guard !self.queue.allItems.contains(where: {$0.url == urlString}) else { continue }
self.queue.addToPending(WebPage(url: urlString, containedLinks: [], status: .pending))
}
// update UI
OperationQueue.main.addOperation {
self.viewDelegate?.reloadTable()
}
// repeat searching process with new urls
self.startSearching()
}
}
queue.loadOperationQueue.addOperation(loadOperation)
}
}
Не могу понять, почему:
Когда я запускаю приложение, экран зависает .Даже если все мои очереди находятся в фоновом режиме (qos: utilities).
Иногда, когда я пытаюсь прокрутить UITableView, я получаю sigabort
из-за индекса вне диапазона , хотя я пытаюсь получить доступ ко всем свойствам в последовательной очереди.
Это исходный код данных:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return presenter.getNumberOfRows()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "WebPageTableViewCell", for: indexPath) as! WebPageTableViewCell
let (url, status) = presenter.getCellContent(at: indexPath.row)
cell.addressLabel.text = url
cell.statusLabel.text = status
return cell
}
И функции из presenter
:
func getNumberOfRows() -> Int {
return queue.allItems.count
}
func getCellContent(at index: Int) -> (url: String, status: String) {
return (url: queue.allItems[index].url, status: queue.allItems[index].status.description)
}