2015-12-18 5 views
0

У меня есть номер NSOperations, который создает некоторые данные асинхронно. Я хочу собрать все результаты в один массив. Поскольку я обращаюсь к массиву на нескольких разных потоках, я поставил блокировку вокруг массива.Как вы можете собирать данные из NSOperations в массив?

NSOperationQueue добавляет данные в массив, но результаты, похоже, пропустят некоторые объекты данных. Кажется, что результаты меняются каждый раз, когда я запускаю его.

Я создал упрощенный примерный проект, который воссоздает проблему. Код находится в Swift, но я не думаю, что это зависит от Swift.

import UIKit 

class ViewController: UIViewController { 
    let queue = NSOperationQueue() 
    var bucket = [String]() 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     queue.addObserver(self, forKeyPath: "operations", options: NSKeyValueObservingOptions.New, context: nil) 

     for _ in 0..<10 { 
      queue.addOperation(NSBlockOperation { 
       // Let's pretend that creating the "fish" string is actually potentially 
       // expensive and that's why we're doing it in an NSOperation. 
       let fish = "fish" 

       objc_sync_enter(self.bucket) 
       self.bucket.append(fish) 

       let fishCount = self.bucket.count 
       print("Bucket contains \(fishCount) fish" + ((fishCount != 1) ? "es" : "")) 
       objc_sync_exit(self.bucket) 
      }) 
     } 
    } 

    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) { 
     if let keyPath = keyPath { 
      if let object = object as? NSOperationQueue { 
       if object == queue && keyPath == "operations" { 
        if queue.operationCount == 0 { 
         objc_sync_enter(self.bucket) 
         let fishCount = bucket.count 
         print("Bucket finally contains \(fishCount) fish" + ((fishCount != 1) ? "es" : "")) 
         objc_sync_exit(self.bucket) 
        } 
       } else { 
        super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context) 
       } 
      } 
     } 
    } 
} 

Результаты варьируются, но часто что-то вроде этого:

Bucket contains 1 fish 
Bucket contains 1 fish 
Bucket contains 1 fish 
Bucket contains 1 fish 
Bucket contains 2 fishes 
Bucket contains 1 fish 
Bucket contains 1 fish 
Bucket contains 3 fishes 

Кроме того, иногда код упал с EXC_BAD_ACCESS на линии self.bucket.append(fish)

Кроме того, линия print("Bucket finally contains \(fishCount) fish" + ((fishCount != 1) ? "es" : "")) в observeValueForKeyPath никогда вызывается. Я не уверен, что это отдельный вопрос или нет.

ответ

1

Вы должны посмотреть на подклассификацию NSOperation, так как это абстрактный класс. См. Это Stackoverflow question для подкласса. Имея это в виду, я бы предположил, что у вас есть свойство идентификатора для каждого экземпляра операции, чтобы вы могли отслеживать свои операции, так, как вы можете узнать, когда все ваши операции завершены. Вы также можете подумать о том, чтобы вытащить этот код из класса контроллера вида и создать класс для обработки вашей рыбы. Кроме того, это поможет вам с инкапсуляцией дальше по дороге, если вы больше не интересуетесь рыбой, но кошками :)

The Concurrency Programming Guide действительно хорошо объясняет основы асинхронного проектирования приложений.

Класс NSOperation - это абстрактный класс, который вы используете для инкапсуляции кода и данных, связанных с одной задачей. Поскольку он абстрактный, вы не используете этот класс напрямую, а вместо этого подклассом или используете один из системных подклассов (NSInvocationOperation или NSBlockOperation) для выполнения фактической задачи. Несмотря на абстрактность, базовая реализация NSOperation включает в себя значительную логику для координации безопасного выполнения вашей задачи. Наличие этой встроенной логики позволяет сосредоточиться на фактической реализации вашей задачи, а не на коде клея, необходимой для обеспечения правильной работы с другими системными объектами.

+0

Я использую NSBlockOperations. Это неправильно? Кроме того, это всего лишь пример проблемы, с которой я столкнулся. Проект, к которому это относится, организован намного лучше. –

+0

Да, вы можете использовать NSBlockOperation, но что делать, если вам нужно отменить операцию? У вас нет способа это сделать. Как вы отслеживаете свои операции. Если вы подклассифицируете NSOperation, я все равно буду использовать блоки. Это выбор дизайна, и использование NSBlockOperation требует от вас большего количества работы на ногах. В любом случае я бы переместил этот код из вашего контроллера. Спросите себя, как вы собираетесь управлять этими операциями? –

+0

Предположим, на данный момент, что мне никогда не нужно отменять эти операции и что мне не нужно знать, когда все операции будут завершены. Проблема размещения 10 рыб в ведро все еще не происходит правильно. Это то, что я хотел бы знать, как делать асинхронно и правильно. –