dispatch_get_current_queue()已被弃用,是否有替代方案来确保CoreData的安全性?

6
许多人都提出了一个类似的问题,但目的却大不相同:CoreData要求您跟踪当前队列、当前线程和当前NSOperationQueue(如果您是NSOperation),如果允许方法调用来自其他类(默认情况下,每个类都允许)。这方面没有“或许”的可能性:这是一项严格的要求。这很好理解,通常也很容易确保。
NSAssert( [NSThread currentThread].isMainThread || myPrivateQueue == dispatch_get_current_queue(), @"You tried to call this method from an external thread, or a queue other than my internal private queue. That's not legal, and will cause data corruption" );

除了苹果公司已经弃用dispatch_get_current_queue()之外,显然是“因为人们滥用它来绕过GCD中缺失的功能/他们不理解的GCD部分”。

注:根据苹果的头部注释,我上面使用的dispatch_get_current_queue()似乎是正确且非滥用的:整个重点是我正在检查队列是否是我创建的私有队列(苹果声称这是可接受的用法)。

撇开仅因其实现中存在缺陷而废弃某些东西的智慧 :( ...有没有人找到了苹果公司删除它的解决方法。特别是:对于CoreData,您必须跟踪队列-还有其他方法可以做到这一点吗?

(这很重要,因为:对于CoreData,如果允许某些东西意外调用这样的方法,您将不会得到“崩溃”,而是会得到“数据损坏”,这将在未来的某个时候显示出来,当时已经太迟修复它)


我一直在使用dispatch_queue_set_specific()dispatch_get_specific(),用于我在这里提出的应用程序:https://dev59.com/J2cs5IYBdhLWcg3wfEHa,并且已经成功地识别了特定队列。答案中Jody的评论提到了在Core Data中使用类似的东西。 - Brad Larson
@BradLarson dispatch_get_specific() 非常有趣,谢谢。我不确定如果在不在 block 内调用它会发生什么(在正确的执行中,你应该在 block 内,但我想知道当它出错时会发生什么,因为这是我试图捕获/断言/记录的设置)。 - Adam
我不知道在块中是否重要。它测试的只是分配给此代码运行的特定队列的密钥。正如我所展示的那样,我将其用于保证在特定队列上同步执行代码的函数,无论是通过同步调度块到队列还是直接在该队列上运行代码。 - Brad Larson
3
dispatch_get_current_queue()已经被弃用,因为API的设计有缺陷,而不是实现有问题。你应该使用dispatch_get_specific()作为替代功能。请注意,这是为了满足你的使用需求。 - das
@BradLarson 如果您将其添加为答案,我会将其标记为正确。SimpleMan的答案很好,但我认为它更像是一种变通方法而不是直接解决方案。 - Adam
2个回答

4

performBlock 总是在正确的线程中运行。只需使用:

[yourManagedObjectContext performBlock:^{//do your stuff here}];

你不再需要追踪当前上下文,因为你的MOC知道哪个线程最先启动了该MOC。当你使用performBlockperformBlockAndWait时,是安全的。

这是一个好主意,但在复杂的情况下似乎存在线程锁定和性能问题——所谓的“问题”指的是“可能是CoreData问题,也可能是我们代码库中的错误,可能两者都有”。我的首要步骤之一是开始大量使用断言来找出为什么偶尔会出现“CoreData在看似无争议的互斥锁上挂起数十秒”的情况。 - Adam
...所以我正在尝试减少我们对于“针对所有事情始终执行块”依赖,并找出实际发生和未发生的情况。 - Adam
据我所知,“始终对所有事情执行performBlock:”是做事的推荐方式。dispatch_get_current_queue()在libdispatch工作方式中基本上是无用的,因为有一小部分“全局”队列和其他队列最终会将操作定位到这些队列上执行。这使得该调用无用,因为当前队列是哪个?它被排队的那个队列吗?还是它正在执行的全局队列?排队者是你所在队列的仲裁者。在这种情况下,performBlock:是排队者,并且是唯一知道你在哪个队列上的方法。 - ipmcc

0
在我的特定情况下,我将所有的performBlock调用替换为:
  1. 将所有CoreData操作移动到一个单独的类中
  2. 创建一个私有的dispatch_queue
  3. 将所有内部调用合并为一个调用
  4. 单个调用在dispatch_async( (private internal queue) )中包装自己
    1. ...并且在dispatch中的第一件事是 "if( self.privateMOC == nil )" ...并创建本地MOC,保证MOC只能在那个队列上访问和创建
发生了两件事:
  1. 所有的互斥锁/锁挂起都消失了(虽然这可能是巧合 - 如果锁仍然被死锁,我稍后会回报)
  2. 性能似乎比使用performBlock:时更好
    1. 注意:我们有很多performBlock调用,并且经常执行此操作(需要以相当高的频率更改CoreData MOC内容的敏感数据)。我不知道在正常应用程序中是否会有明显的差异

...所以,就目前而言,对于我的需求来说:这已经证明是一个不错的解决方案。我并不认为这是针对问题的“正确”通用答案,但是……我向任何发现“performBlock”不如它表面上看起来那么神奇的人推荐它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接