如何阻止弧释放由2个线程共享的对象两次



我们的应用程序会在我们编写的启用电弧的库中经历双重发布崩溃。运行仪器后,我们发现双重释放发生在由2个线程访问的对象上。

首先,在初始化方法中分配了有问题的对象

在两个线程之外分配
objectX = [[NSData alloc] initWithBytes:barcodeBytes length:sizeof(barcodeBytes)];

螺纹a启动,并将objectx添加到nsdictionary。

线程B将ObjectX分配给本地NSDATA指针,并使用RemoveObjectAtaTIndex

从共享的Nsdictionary中删除ObjectX

我们通过运行NSzombie所注意到的是,ObjectX现在已经两次自动释放。看来一个发行版直接在对象上发出,并且当释放包含其的Nsdictionary时,请间接完成另一个版本。

第一个版本:

   0 libobjc.A.dylib -[NSObject release]
   1 libobjc.A.dylib (anonymous namespace)::AutoreleasePoolPage::pop(void*)
   2 libobjc.A.dylib (anonymous namespace)::AutoreleasePoolPage::tls_dealloc(void*)
   3 libsystem_pthread.dylib _pthread_tsd_cleanup
   4 libsystem_pthread.dylib _pthread_exit
   5 libsystem_pthread.dylib pthread_exit
   6 Foundation +[NSThread exit]
   7 TestApp 0x348e72
   8 Foundation __NSThread__main__
   9 libsystem_pthread.dylib _pthread_body
  10 libsystem_pthread.dylib _pthread_start
  11 libsystem_pthread.dylib thread_start

第二版:

   0 libobjc.A.dylib -[NSObject release]
   1 CoreFoundation CFRelease
   2 CoreFoundation -[__NSDictionaryM dealloc]
   3 libobjc.A.dylib objc_object::sidetable_release(bool)
   4 libobjc.A.dylib -[NSObject release]
   5 libobjc.A.dylib (anonymous namespace)::AutoreleasePoolPage::pop(void*)
   6 libobjc.A.dylib (anonymous namespace)::AutoreleasePoolPage::tls_dealloc(void*)
   7 libsystem_pthread.dylib _pthread_tsd_cleanup
   8 libsystem_pthread.dylib _pthread_exit
   9 libsystem_pthread.dylib pthread_exit
  10 Foundation +[NSThread exit]
  11 TestApp 0x348e72
  12 Foundation __NSThread__main__
  13 libsystem_pthread.dylib _pthread_body
  14 libsystem_pthread.dylib _pthread_start
  15 libsystem_pthread.dylib thread_start

最终结果是objectX被释放到应有的两倍,我们看到崩溃。有趣的是,我们只在ARM64设备上看到了这一点。

当您将对象存储在两个线程中时,您也隐式保留该对象,因此发行计数匹配。相反,我会看看您是否在某处弄乱了围栏,尤其是如果您不使用GCD。请记住,ARM是一个较弱的架构,因此与x86相比,进入内存订购问题要容易得多。

螺纹A启动,并将objectx添加到nsdictionary。

线程B将ObjectX分配给本地NSDATA指针,并从共享的Nsdictionary

中删除ObjectX

但是您的问题就在那里。您有两个对象objectX和词典,这些对象被两个不同的线程触摸。这是一个严重的危险,如果您不确切地知道自己在做什么以及如何采取适当的预防措施,那么您就不应该这样做。

,没有看到字典的同步代码很难说。如果数据是不可变的,则可以在第二个线程中创建副本。这将确保它与被删除的字典是一个不同的对象。

还要确保当您突变字典时,所有读取线程都在等待。

最新更新