我们的应用程序在我们编写的启用了ARC的库中遇到双重版本崩溃。在运行检测之后,我们发现双重释放发生在由2个线程访问的对象上。
首先在初始化方法
中的2个线程之外分配有问题的对象objectX = [[NSData alloc] initWithBytes:barcodeBytes length:sizeof(barcodeBytes)];
启动线程A并将objectX添加到NSDictionary。
线程B将ObjectX分配给本地NSData指针,并使用removeObjectAtIndex从共享的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设备上看到了这一点。
答案 0 :(得分:0)
当您将对象存储在两个线程中时,您也会隐式保留该对象,因此释放计数匹配。相反,我会看看你是否正在搞乱某个地方的线程,特别是如果你没有使用GCD。请记住,ARM是一种弱有序的架构,因此比x86更容易进入内存排序问题。
答案 1 :(得分:0)
启动线程A并将objectX添加到NSDictionary。
线程B将ObjectX分配给本地NSData指针,并从共享的NSDictionary中删除ObjectX
但那里有你的问题。您有两个对象objectX
和字典,它们被两个不同的线程触及。这是一个严重的危险,如果你不确切知道自己在做什么,以及如何采取适当的预防措施,你根本就不应该这样做。
答案 2 :(得分:0)
没有看到字典的同步代码很难说。如果数据是不可变的,您可以在第二个线程中创建副本。这将确保它与被删除的字典不同。
还要确保在改变字典时,所有读取线程都在等待。