Как сообщить результаты между зависимостями NSOperation?

Новая среда Cloud Kit использует NSOperation для CRUD. Результаты этих операций возвращаются в блоках. Например:

let fetchOperation = CKFetchRecordsOperation(recordIDs: [recordID1, recordId2])

fetchOperation.fetchRecordsCompletionBlock = {(dict: NSDictionary!, error: NSError!) -> Void in
            // dict contains RecordId -> Record            
            // do something with the records here (if no error)
        }

Я хочу связать несколько из этих операций (зависимостей) и передать результат операции следующей операции в цепочке. Упрощенный пример, чтобы проиллюстрировать это (псевдо-код!):

let fetchOperation1 = CKFetchRecordsOperation(recordIDs: [recordID1, recordId2])

fetchOperation1.fetchRecordsCompletionBlock = {(dict: NSDictionary!, error: NSError!) -> Void in
            if error {
              // handle error
            } else {
               // dict contains RecordId -> Record            
               // let pretend our records contain references to other records
               // that we want to fetch as well
               fetchOperation.operationResult = 
                   dict.allValues().map(
                      { $0.getObject("referencedRecordId"}
               )
            }
        }

let fetchOperation2 = CKFetchRecordsOperation(recordIDs: fetchOperation1.operationResult)

fetchOperation2.fetchRecordsCompletionBlock = {(dict: NSDictionary!, error: NSError!) -> Void in
            if error {
              // handle error
            } else {
              // dosomething
            }
        }

fetchOperation2.addDependency(fetchOperation2)

Но выше псевдокод никогда не может работать, поскольку fetchOperation1.operationResult еще не назначен при инициализации fetchOperation2. Вы можете вложить инициализацию fetchOperation2 в fetchOperation1 completeBlock, но чем вы отключаете функциональность зависимостей NSOperation, которую я пытаюсь использовать здесь.

Итак, я ищу чистый, читаемый, стандартный (без реактивного cocoa и такого) решения для работы, поскольку зависимости NSOperation передают данные в цепочке.

Ответ 1

Я помню, когда был введен первый NSOperation, и мне пришлось написать вступительную статью для сайта ADC, которая сначала загрузила бы некоторые фотографии, а затем превратила их в плакат. Я столкнулся с аналогичными проблемами: использование зависимостей для управления порядком, но затем я обнаружил, что мне пришлось передавать имена файлов изображений в зависимую операцию.

Это было много лет назад, и в то время у меня были подклассы NSOperation для каждой задачи. Я устанавливал зависимости между ними и добавлял делегат к операциям, которым нужно было передавать результаты от более ранней операции. В методе делегата объект контроллера будет извлекать результаты из свойства первой операции и устанавливать их через свойство на втором.

Возможно, лучшим решением является то, чтобы отношения между операциями были явными не только с точки зрения зависимостей, но и с точки зрения передачи данных. Таким образом, вы можете создать подклассы NSOperation, которые передают данные в следующий NSOperation как часть стандартной операции, или — более элегантно — извлекать данные из завершенной операции.

Чтобы сделать это более конкретным: операция B зависит от завершения A. A генерирует ресурс R, который необходим B для запуска. Вы добавляете свойство к B, которое ссылается на объект A. Когда B запускается, он просто извлекает R из объекта A.

Если вы предпочитаете не создавать подклассы операций и просто хотите избежать вложенности блоков, вы можете рассмотреть возможность использования механизма очередей, который дает вам немного больше контроля, например CDEAsynchronousTaskQueue.

Ответ 2

Просто объявите вторую операцию над первым блоком, чтобы вы могли установить идентификаторы записи для этого, как показано в этом правиле:

let fetchOperation1 = CKFetchRecordsOperation(recordIDs: [recordID1, recordId2])
let fetchOperation2 = CKFetchRecordsOperation()

fetchOperation1.fetchRecordsCompletionBlock = {(dict: NSDictionary!, error: NSError!) -> Void in
            if error {
              // handle error
            } else {
               // dict contains RecordId -> Record            
               // let pretend our records contain references to other records
               // that we want to fetch as well
               fetchOperation2.recordIDs = 
                   dict.allValues().map(
                      { $0.getObject("referencedRecordId"}
               )
            }
        }

fetchOperation2.fetchRecordsCompletionBlock = {(dict: NSDictionary!, error: NSError!) -> Void in
            if error {
              // handle error
            } else {
              // dosomething
            }
        }

fetchOperation2.addDependency(fetchOperation1)

Также, если вы получите ошибку в своем первом блоке, вы должны отменить все операции в очереди. Каждый блок все равно будет вызываться, но у него будет отменен набор NSError. Таким образом, вам не нужен какой-либо специальный финальный блок, который может иметь пользовательские операции.