Apple's multithreading docs不会将NSIndexPath
列为线程安全与否!作为一个不可变的类,我通常希望它是线程安全的。
以前,我确信文档用于说明NSIndexPath
实例已共享且全局唯一。现在似乎已经消失了,让我怀疑iOS5 / Mac OS X 10.7的设计已经修改。
我在Mac OS X 10.6(Snow Leopard)上看到了很多来自客户的崩溃报告,这些报告似乎在试图访问索引路径时崩溃。因此我想知道:实际的实例是线程安全的,但是将它们从共享缓存中拉出来的逻辑不是吗?有没有人有任何见解?
这是一个示例堆栈跟踪BTW:
Dispatch queue: com.apple.root.default-priority
0 libobjc.A.dylib 0x96513f29 _cache_getImp + 9
1 libobjc.A.dylib 0x965158f0 class_respondsToSelector + 59
2 com.apple.CoreFoundation 0x948bcb49 ___forwarding___ + 761
3 com.apple.CoreFoundation 0x948bc7d2 _CF_forwarding_prep_0 + 50
4 com.apple.Foundation 0x994b10c5 -[NSIndexPath compare:] + 93
5 com.apple.Foundation 0x99415686 _NSCompareObject + 76
6 com.apple.CoreFoundation 0x948af61c __CFSimpleMergeSort + 236
7 com.apple.CoreFoundation 0x948af576 __CFSimpleMergeSort + 70
8 com.apple.CoreFoundation 0x948af38c CFSortIndexes + 252
9 com.apple.CoreFoundation 0x948fe80d CFMergeSortArray + 125
10 com.apple.Foundation 0x994153d3 _sortedObjectsUsingDescriptors + 639
11 com.apple.Foundation 0x994150d8 -[NSArray(NSKeyValueSorting) sortedArrayUsingDescriptors:] + 566
对我来说,这是一个NSIndexPath
实例,试图将自己与已解除分配的实例进行比较。
答案 0 :(得分:4)
到目前为止,我的最佳答案是我怀疑:
从OS X 10.7和iOS 5开始,NSIndexPath
是线程安全的。在此之前,实例是线程安全的,因为它们是不可变的,但现有实例的共享检索不是。
对于按需返回索引路径的方法,我这样做了:
- (NSIndexPath *)indexPath;
{
NSIndexPath *result = … // create the path as appropriate
return [[result retain] autorelease];
}
自实现最后一行代码以来,我们没有更多来自索引路径的崩溃报告。
索引路径由-indexPathByAddingIndex:
或+indexPathWithIndex:
创建。
我看到的结果让我非常肯定(在10.7 / iOS5之前)这些方法返回现有的NSIndexPath
实例。当前线程不以任何方式保留该实例,因此首先创建实例的线程(在我们的例子中为main)是释放路径(可能通过弹出自动释放池)并使我们的工作线程留下悬空指针,在使用时会崩溃,如问题所示。
这有点可怕,因为如果我的分析是正确的,我添加的retain
/ autorelease
舞蹈只是将一种竞争条件替换为另一种不太可能的竞争条件。
在10.7 / iOS5之前,我只能想到一个真正的解决方法:限制所有索引路径创建到主线程。如果这样的代码被大量调用,那可能会相当慢,因此可以通过维护自己的某种实例缓存来为后台线程使用来改进 - 以内存为代价。如果缓存保留了一个路径,那么你知道它不会被主线程解除分配。
答案 1 :(得分:1)
Apple没有特别列出NSIndexPath作为线程安全,但是他们确实说不可变类通常是安全的,而可变类通常不是。由于NSIndexPath是不可变的,因此可以安全地假设它是线程安全的。
但是“线程安全”并不意味着它在一个线程释放之前不会导致崩溃,而是在另一个线程上使用它之前。线程安全通常只是意味着它的mutator方法包含锁定以防止由于两个线程同时设置属性而导致的毛刺(这就是为什么没有mutator方法的类通常是线程安全的,尽管懒惰的getter和共享实例也会导致问题)。
听起来你的错误更可能是由于使用自动释放池或其他一些机制导致你的对象在你控制范围之外被释放。您应该确保任何并发访问的对象都存储在长期类的属性中,以便您可以控制它们的生命周期。
创建一个自动释放的对象并在删除所有强引用之后从另一个线程访问它是一个危险的赛车游戏,无论所讨论的对象是否是“线程安全的”,它都可能导致难以追踪的崩溃”