Dispatch once(dispatch_once)singleton冻结/锁定目标c

时间:2015-02-18 23:58:03

标签: ios objective-c xcode singleton grand-central-dispatch

这行代码是在我的awakeFromFetch方法中调用的,该方法位于实现NSManagedObject的自定义托管对象中。这一行尤其是调用我的名为sharedManager的单例网络管理器类。

[self setSync:(![[WKNetworkManager sharedManager] objectHasPendingRequests:self.objectID]) ];

将按下面所示命中dispatch_once块。请注意,它以良好的方式实现,如here所示:

enter image description here

然后dispatch_once调用转到once.h,并在此突出显示的行上冻结:

dipatch_once deeper

这是堆栈跟踪:

stack trace

尝试加载以前保存的网络队列文件时,所有这些都会发生。应用程序完全关闭以保存,然后再次启动,然后发生冻结/锁定。

我甚至尝试使用此代码来解决问题here,但它无法解决问题。但是改变这个可能无关紧要,因为我原来的dispatch_once代码已经很好地工作了很长时间。只是在这种特殊情况下。

if ([NSThread isMainThread]) 
{
    dispatch_once(&onceToken, ^{
    stack = [[KACoreDataStack alloc] init];});
} 
else 
{
    dispatch_sync(dispatch_get_main_queue(), ^{
    dispatch_once(&onceToken, ^{
    stack = [[KACoreDataStack alloc] init];});
});
}

到目前为止,这些是我解决此类问题的来源:

感谢您的帮助!

2 个答案:

答案 0 :(得分:6)

该死的儿子,这是一个很好的问问题。

这里的问题是对dispatch_once的递归调用将会死锁。如果您需要确切的详细信息,请you can find them with the source here。所以你需要重写以避免这种情况。

我的观点是,在-loadQueueFromDisk这个dispatch_once电话会议中,有一个架构失误。你绝对不会做任何在执行时间内可能无限制的事情,如在{{1}}内从磁盘加载某些内容。你想要做的是创建一个可寻址的单例并返回的绝对最小值。然后,给它一个内部状态“正在初始化”,并将磁盘加载的东西运送到某个地方的非阻塞队列。这样,所有不依赖于从磁盘加载的东西的东西都可以继续,并且所有依赖于从磁盘加载的东西的东西都可以将自己添加到磁盘加载所在的队列中,或者每隔几百毫秒检查一下如果它还处于“已初始化”状态,或者沿着那些线路。

答案 1 :(得分:0)

感谢alexcurylo的帮助,排除loadFromDisk作业解决了这个问题。为了使人们不会感到困惑,只需要使用下面的代码,我正在排队一个从磁盘加载数组(充当保存的用户请求队列)的作业。您可以对任何作业进行排队,例如加载图像等。我的sharedManager类中的以下代码(从磁盘加载的位置)起作用,如下所示:

-(void) loadQueueFromFileByQueueing
{
  //Create a Queue
  /* Note that fileStrQueue should be added to the .h file as NSOperationQueue *fileStrQueue;*/
  fileStrQueue = [[NSOperationQueue alloc] init];

  //Create an  operation which will invoke method loadQueueFromDisk
  NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(loadQueueFromDisk) object:nil];

  //Add this operation to the queue
  [fileStrQueue addOperation:operation];

  //Releasing the above operation as it is already retained by the queue.
  // [operation release]; // If you are using ARC, this won't be necessary
}