【发布时间】:2016-06-05 16:08:35
【问题描述】:
在 Crashlytics 中,我看到用户很少遇到的崩溃。有问题的代码看起来像这样......
- (void)updateIsAnsweredField:(NSArray *)moduleItemsList
{
if (moduleItemsList && self.answeredItems && self.answeredItems.count > 0) {
for (ModuleItem * item in moduleItemsList) { // "Collection was mutated while being enumerated"
if ([item isKindOfClass:[ModuleItem class]] && [item shouldCheckIfAnswered]) {
item.answered = [self isAnsweredItem:item.moduleID];
}
}
}
}
Crashlytics给出的错误可以在上面代码sn-p的注释中看到。
我认为有几种方法可以解决这个问题。
1) 将函数内的所有内容包装在 @synchronized(moduleItemsList) {} 中。这是解决问题的理想方法吗?我听说@synchronized 非常慢,尽可能避免使用它。
2) 创建一个副本,如 NSMutableArray *copyModuleItemsList = [moduleItemsList mutableCopy];。然后列举。这能解决问题吗?我认为它会解决这个特定问题,但会有另一个问题,不是吗?那就是...最后,当我们将副本分配回原来的 moduleItemsList = copyModuleItemsList; 时,moduleItemsList 可能同时在不同的线程上发生了变化。
3) 将传入的:(NSArray *)moduleItemsList 跟踪到将其作为属性持有的任何人。然后覆盖getter以使用dispatch_sync,并覆盖setter以使用dispatch_barrier_async。但是,不能保证原始数组是可以覆盖其 getter 和 setter 的任何类的属性。实际上,这些都没有任何意义,因为我们不会专门更改该数组吗?
我有点困惑。任何人都可以在这件事上提供帮助吗? #1 是我想要的选项吗?
编辑:添加更多代码
[item shouldCheckIFAnswered]:
这只是检查 ModuleItem 类中存在的 @property 值。 if self.moduleType == ModuleTypeSuchAndSuch
isAnsweredItem::
- (BOOL)isAnsweredItem:(NSString *)moduleID
{
if (!self.answeredItems) {
return NO;
}
return [self.answeredItems containsObject:moduleID];
}
【问题讨论】:
-
第 1 项可能不会修复它,因为数组的修饰符在同步边界之外(同步仅在修改的每个人都同步时才有效)。您不需要可变副本;副本本身只需要是静态副本(您自己不会对其进行变异),即
[moduleItemsList copy]。不过,我不明白将其复制回来的原因,因为它是一个对象数组,更改对象也会影响其他容器中的对象——这只是它们的浅拷贝。 -
仅添加
copy并不能解决问题——在复制过程中moduleItemsList仍有可能在另一个线程中发生变异。这也会导致崩溃。 -
@BorisVerebsky 你会怎么解决这个问题?
-
你在 shouldCheckIfAnswered 和 isAnsweredItem 方法中做了什么?你能提供这两种方法的代码吗
-
@Arun 添加到原始帖子中。
标签: objective-c multithreading grand-central-dispatch synchronized