因此,在了解完一段时间的完成块之后,我非常喜欢使用完成块。我喜欢封闭,我喜欢能够传递几乎任何我想要的东西的能力。

作为线程编程的新手,我一直远离GCD和NSOperation,但是最近我不得不将异步编程的程序更新为Core Data,并且我开始对我的“所有完成块一直感到怀疑” ”方法。

因此,这是我向自己提出疑问的一个示例:我有一系列可能相当大的数据(图像,声音,视频,您拥有什么)上传到某个地方的服务器。这些数据的元数据存储在Core Data中,我使用一个时间戳来确定应该上传哪些对象。所有这些上传应按顺序进行。

我编写的代码本质上基本上只是一个带有完成块的函数,该函数在该块的末尾具有对自身的调用,如下所示:

(void)uploadAllAsynchronously {
  ... // First figure out what to upload based on core data
  // Here comes the completion block in question
  void(^blk)(BOOL) = ^(BOOL)uploadSuccess {
    ... // if upload successful, update core data to mark what has been uploaded
    [self uploadAllAsynchronously]; // Recursively calls the function that contains this block.  I actually have a weak self, or if that fails to break a retain cycle, I should be able to pass in a NSManagedObjectContext as an argument.
  }
  [NSURLConnection sendAsynchronousRequest:... queue:... completionHandler:blk];


}

这应该工作,对不对?这里是否存在完全危险的建议我必须使用GCD并处理自己的队列的事情?我之所以这样问,是因为我现在有点麻烦,因为其中的数据可能会因为异步调用而出现不同线程无法正确更新的情况,尽管不能确定我的代码的哪一部分是罪魁祸首。

提前致谢。

最佳答案

您的街区类型错误。

作为the documentation

+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse *, NSData *, NSError *))handler

显示,完成黑色的类型是
void (^) (NSURLResponse *, NSData *, NSError *)


void (^) (BOOL)

您应该将blk更改为类似
void (^blk) (NSURLResponse *, NSData *, NSError *) = ^ (NSURLResponse *response, NSData *data, NSError *error) {
    //...
};

写起来会更时尚
[NSURLConnection sendAsynchronousRequest:theRequest queue:theQueue completionHandler:^ (NSURLResponse *response, NSData *data, NSError *error) {
    //...
}];

与该方法一致的完成模块。

关于在完成处理程序中对NSManagedObjectContext执行操作的问题:这很好,只要传递给NSOperationQueuesendAsynchronousRequest:queue:completionHandler:与创建托管对象上下文的那个相同即可。但是正如the documentation for NSManagedObjectContext 所说

Core Data使用线程(或序列化队列)限制来保护托管对象和托管对象上下文(请参阅“Core Data的并发性”)。这样的结果是,上下文假定默认所有者是为其分配线程的线程或队列,这是由调用其init方法的线程确定的。因此,您不应在一个线程上初始化上下文,然后将其传递给另一个线程。相反,您应该将引用传递给持久性存储协调器,并让接收线程/队列创建一个从中派生的新上下文。

如果传递的队列不是在其上创建托管对象上下文的队列,则必须执行以下操作之一
  • 在创建托管对象上下文的队列上调用-[NSOperationQueue addOperation:]
  • 在正在执行核心数据操作的队列上创建第二个管理对象上下文(具有相同的持久性存储协调器)。
  • 在正在进行核心数据操作的队列上创建第二个管理对象上下文和第二个持久性存储协调器。
  • 使用锁定。

  • Concurrency with Core Data上的文档明确指出,您必须使用线程限制(上面的选项1-3)或使用锁定(上面的选项4)。

    这就是文档关于使用锁的说法:

    如果您选择不使用线程包含模式(即,如果您尝试在线程之间传递托管对象或上下文,依此类推),则必须非常谨慎地进行锁定,因此很可能会抵消您的任何利益否则可能源自多线程。

    这就是文档关于不仅仅具有按线程管理的对象上下文而且还具有按线程的持久性存储协调器的说法:

    这种方法提供了更高的并发性,但付出了更大的复杂性(特别是如果您需要在不同上下文之间传达更改的情况)和增加的内存使用量。

    关于ios - 关于使用完成块入队,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14084455/

    10-14 09:01
    查看更多