我一直试图解决这个崩溃已近一周了。应用程序崩溃,没有任何异常或堆栈跟踪。在僵尸模式下通过仪器运行时,应用程序不会以任何方式崩溃。
我有一个在不同线程上调用的方法。 修复崩溃的解决方案正在取代
[self.mutableArray removeAllObjects];
带
dispatch_async(dispatch_get_main_queue(), ^{
[self.searchResult removeAllObjects];
});
我认为这可能是一个时间问题,所以我尝试同步它,但它仍然崩溃了:
@synchronized(self)
{
[self.searchResult removeAllObjects];
}
这是代码
- (void)populateItems
{
// Cancel if already exists
[self.searchThread cancel];
self.searchThread = [[NSThread alloc] initWithTarget:self
selector:@selector(populateItemsinBackground)
object:nil];
[self.searchThread start];
}
- (void)populateItemsinBackground
{
@autoreleasepool
{
if ([[NSThread currentThread] isCancelled])
[NSThread exit];
[self.mutableArray removeAllObjects];
// Populate data here into mutable array
for (loop here)
{
if ([[NSThread currentThread] isCancelled])
[NSThread exit];
// Add items to mutableArray
}
}
}
NSMutableArray的这个问题不是线程安全的吗?
答案 0 :(得分:85)
没有
它不是线程安全的,如果你需要从另一个线程修改你的可变数组,你应该使用NSLock
来确保一切按计划进行:
NSLock *arrayLock = [[NSLock alloc] init];
[...]
[arrayLock lock]; // NSMutableArray isn't thread-safe
[myMutableArray addObject:@"something"];
[myMutableArray removeObjectAtIndex:5];
[arrayLock unlock];
答案 1 :(得分:36)
正如其他人已经说过的那样,NSMutableArray不是线程安全的。如果有人想要在线程安全的环境中实现多于removeAllObject,除了使用锁定之外,我将使用GCD提供另一种解决方案。您需要做的是同步读取/更新(替换/删除)操作。
首先获取全局并发队列:
dispatch_queue_t concurrent_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
阅读:
- (id)objectAtIndex:(NSUInteger)index {
__block id obj;
dispatch_sync(self.concurrent_queue, ^{
obj = [self.searchResult objectAtIndex:index];
});
return obj;
}
对于插入:
- (void)insertObject:(id)obj atIndex:(NSUInteger)index {
dispatch_barrier_async(self.concurrent_queue, ^{
[self.searchResult insertObject:obj atIndex:index];
});
}
来自Apple Doc的dispatch_barrier_async:
当屏障块到达专用并发队列的前面时,它不会立即执行。相反,队列等待,直到其当前正在执行的块完成执行。此时,屏障块自行执行。在屏障块完成之后,在屏障块之后提交的任何块都不会执行。
类似于删除:
- (void)removeObjectAtIndex:(NSUInteger)index {
dispatch_barrier_async(self.concurrent_queue, ^{
[self.searchResult removeObjectAtIndex:index];
});
}
编辑:实际上我今天找到了另一种更简单的方法,即使用GCD提供的串行队列来同步对资源的访问。
来自Apple Doc Concurrency Programming Guide > Dispatch Queues:
当您希望任务按特定顺序执行时,串行队列非常有用。串行队列一次只执行一个任务,并始终从队列的头部提取任务。您可以使用串行队列而不是锁来保护共享资源或可变数据结构。与锁不同,串行队列确保以可预测的顺序执行任务。只要您将任务异步提交到串行队列,队列就永远不会死锁。
创建序列队列:
dispatch_queue_t myQueue = dispatch_queue_create("com.example.MyQueue", NULL);
将任务异步调度到串行队列:
dispatch_async(myQueue, ^{
obj = [self.searchResult objectAtIndex:index];
});
dispatch_async(myQueue, ^{
[self.searchResult removeObjectAtIndex:index];
});
希望它有所帮助!
答案 2 :(得分:19)
除NSLock
也可以使用@synchronized
(条件对象)之外,您只需要确保数组的每次访问都包含在{{1}中使用相同的对象充当 condition-object ,如果您只想修改同一个数组实例的内容,那么您可以将该数组本身用作 condition-object ,另一方面,你将不得不使用你知道不会消失的其他东西,父对象,即self,是一个很好的选择,因为对于同一个数组,它总是相同的。
@synchronized
属性中的原子只会使数组线程安全而不修改内容,即@property
= ...是线程安全的,但self.mutableArray
不是。
答案 3 :(得分:12)
__weak typeof(self)weakSelf = self;
@synchronized (weakSelf.mutableArray) {
[weakSelf.mutableArray removeAllObjects];
}
这篇精彩的文章将解释这一切:
答案 4 :(得分:5)
由于提到了串行队列:使用可变数组,只是询问“它是否是线程安全的”是不够的。例如,确保removeAllObjects没有崩溃是好的,但是如果另一个线程试图同时处理该数组,它将在或之前处理数组 所有元素都被删除了,你真的必须考虑应该是什么行为。
创建一个负责此数组的类+对象,为其创建一个串行队列,并通过该串行队列上的类执行所有操作是最简单的方法,可以在不通过同步问题让您的大脑受到伤害的情况下做到正确。
答案 5 :(得分:0)
几乎NSMutable类对象不是线程安全的。
答案 6 :(得分:0)
所有NSMutablexxx类都不是线程安全的。包含get,insert,remove,add和replace的操作应该与NSLock一起使用。这是一个由apple提供的线程安全和线程不安全的类列表:Thread Safety Summary