1

因此,在了解完成块之后,我很喜欢使用完成块。我喜欢关闭,我喜欢在任何地方传递任何想要的东西的能力。关于使用完成块排队

作为线程编程的新手,我一直在远离GCD和NSOperation - 但最近我不得不编程异步更新到核心数据的东西,我开始怀疑我的“全部完成阻止所有的时间“的方法。

所以这里是我自问的一个例子:我有一系列可能相当大的数据(图像,声音,视频,你有什么)上传到服务器的地方。这些数据的元数据存储在核心数据中,并且我有一个时间戳用于决定应该上传哪些对象。所有这些上传应该按顺序进行。

我所编码的是一种本质上基本上是一个在它完成块,在该块的一端有一个电话给自己,这样的功能:

(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并处理我自己的队列?我问这是因为我现在有点麻烦,可能数据中会出现不同的线程,由于异步调用而不能正确更新,但不知道我的代码的哪一部分是罪魁祸首。

在此先感谢。

+0

请记住,完成处理程序通常不会在后台线程上运行。通常(包括'[NSURLConnection sendAsynchronousRequest:queue:completionHandler:]')完成块在主线程上执行。所以你根本不用担心线程安全。如果主线程在后台线程完成下载时处于繁忙状态,则在执行完成块之前,主线程将处于空闲状态。 –

+0

我发现有一种情况嵌套完成块会有问题,并可能导致死锁:http://www.cocoawithlove.com/2010/06/avoiding-deadlocks-and-latency-in.html – Victor

回答

1

你块是错误的类型。

至于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在完成处理程序:这是好的,只要传递给sendAsynchronousRequest:queue:completionHandler:NSOperationQueue是一样的,其中创建的托管对象上下文的一个。但随着the documentation for NSManagedObjectContext状态

核心数据使用线程(或序列化队列)约束,以保护管理对象和管理对象上下文(请参阅“并发核心数据”)。其结果是上下文假定默认所有者是分配它的线程或队列 - 这由调用其init方法的线程决定。因此,您不应该在一个线程上初始化上下文,然后将其传递给另一个线程。相反,您应该传递对持久存储协调器的引用,并让接收线程/队列创建一个从此派生的新上下文。

如果您传递的队列是不是您所创建的管理对象范围内的人,你必须做的队列在管理对象方面是以下

  1. 呼叫-[NSOperationQueue addOperation:]之一创建。

  2. 在发生核心数据操作的队列上创建第二个托管对象上下文(使用相同的持久存储协调器)。

  3. 在发生核心数据操作的队列上创建第二个管理对象上下文和第二个持久存储协调器。

  4. 使用锁定。

Concurrency with Core Data的文件清楚地表明,你必须使用线程约束(以上选项1-3)或使用锁定(上面的选项4)。

这就是文档必须说关于使用锁:

如果您选择不使用线程容纳图案,也就是说,如果你试图在线程之间传递管理对象或情境,等等你必须非常小心锁定,因此你可能会否定任何你可能从多线程中获得的好处。

这就是文档不得不说具有不只是每个线程的管理对象上下文也每个线程的持久性存储协调员:

这种做法在更大的代价提供了更大的并发性复杂性(特别是如果您需要在不同的上下文之间传递更改时)和增加的内存使用率。

+0

我的主要事情因此文档是这样的:在WWDC 2012 Session 214视频中,它看起来像“因此,不应该在一个线程上初始化上下文,然后将其传递给另一个线程”不再适用于每个上下文(只要它是MainQueue或PrivateQueue之一)实际上管理自己的队列,不管它是什么线程。我不知道你是否看过那段视频,但如果你有,我是否正确解释了视频中的主持人? – Victor

+0

我目前无法访问该WWDC视频,但有关[核心数据并发性](https://developer.apple)的文档。com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdConcurrency.html#// apple_ref/doc/uid/TP40003385)很清楚,如果您使用多个线程/队列,则必须使用线程限制(具有每个线程管理的对象上下文(以及潜在的每线程持久存储协调器))或使用锁定。 (请参阅我的编辑。) –

+0

是的,这就是我感到困惑 - 文档似乎并不像我在WWDC视频中的解释一样。我对视频的印象(我认为这是最近的)说,现在核心数据处理自己的队列,所以除非绝对必要,否则不要在核心数据呼叫中使用GCD。或者我认为 - 在任何操作系统的线程中,我都是新手。 – Victor

1

是此代码应工作..注:我会重新命名方法的话.. uploadIfNeeded可能 - 因为它不总是一味上传东西...