问题 如何在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's 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)

但是上面的伪代码永远不会起作用,因为在初始化fetchOperation2时尚未分配fetchOperation1.operationResult。你可以在fetchOperation1的completionBlock中嵌入fetchOperation2的init,但是你放弃了NSOperation的依赖功能,我试图在这里使用它。

因此,我正在寻找一个干净,可读,标准(没有反应性可可等)的解决方案,NSOperation依赖项在其链中传递数据。


943
2018-06-25 09:36


起源

如果在第一个操作的完成块中创建第二个操作,则第二个操作将始终在第一个操作之后运行。如果您仍然按顺序执行,为什么分配依赖关系是否重要? - Tom Harrington
@TomHarrington这是一个简化的例子。我有一个操作A,它将结果发送到操作B1和操作B2,需要将它们的结果发送到操作C.嵌套变得非常快(也称为'回调地狱')并且需要一个模式使这个可读'复杂' '这样的案件。 - Ward Bekker


答案:


我记得当 NSOperation 最初是介绍,我必须为ADC网站写一篇介绍文章,首先下载一些照片,然后将它们渲染成海报。我在那里遇到了类似的问题:使用依赖关系控制顺序,但后来发现我必须将图像文件名传递给依赖操作。

那是很多年前的事了,当时我有 NSOperation 每个任务的子类。我在它们之间设置了依赖关系,并为需要从早期操作传递结果的操作添加了委托。在委托方法中,控制器对象将从第一个操作的属性中检索结果,并通过第二个操作的属性设置它们。

也许更好的解决方案是使操作之间的关系显式化,不仅在依赖性方面,而且在数据传递方面。所以你可以创造 NSOperation 将数据传递给下一个的子类 NSOperation 作为标准操作的一部分,或者 - 更优雅 - 从已完成的操作中提取数据。

为了使其更具体:操作B取决于A是否完整。 A生成B运行所需的资源R.您向B添加一个引用A对象的属性。当B运行时,它只是从A对象中检索R.

如果您不想创建操作子类,并且只是想避免嵌套,您可以考虑使用排队机制来提供更多控制,例如 CDEAsynchronousTaskQueue


6
2018-06-26 06:52



WWDC 2015高级NSOperations有很多可以提供的。 - highmaintenance
不幸的是它没有。使用临时缓存文件在网络操作和解析操作之间共享数据是实现共享数据的唯一示例。这就像隐藏的全球单身人士 - Mindaugas


只需在第一个块上面声明第二个操作,这样你就可以设置recordIDs来获取它,正如你对这个例子的编辑所示:

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's 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设置为取消。因此,您不需要任何特殊的最终块,就像您可能拥有自定义操作一样。


2
2018-03-30 22:40





在这里,您可以找到4种不同的方法来在Swift中的两个Operations之间传递数据。

在swift中的操作之间传递数据的4种方法


1
2017-09-12 07:17