Как сообщить результаты между зависимостями 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. Таким образом, вам не нужен какой-либо специальный финальный блок, который может иметь пользовательские операции.
Ответ 3
Здесь вы можете найти 4 разных подхода к передаче данных между двумя операциями в Swift.
4 способа передачи данных между операциями в swift