我如何解决 "Collection was mutated while being enumerated"、@synrchonized、mutableCopy 或其他问题?
How can I solve "Collection was mutated while being enumerated", @synrchonized, mutableCopy, or something else?
在 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 给出的错误可以在上面代码片段的注释中看到。
我想有几种方法可以解决这个问题。
1) 将函数内的所有内容包装在@synchronized(moduleItemsList) {} 中。这是解决问题的理想方法吗?我听说 @synchronized 非常慢,尽可能避免使用它。
2) 创建一个副本 NSMutableArray *copyModuleItemsList = [moduleItemsList mutableCopy];
。然后枚举那个。这会解决问题吗?我认为它会解决这个特定问题,但会有另一个问题不是吗?那是...最后,当我们将副本分配回原始 a la moduleItemsList = copyModuleItemsList;
时,moduleItemsList 可能同时在不同的线程上发生了变化。
3) 将传入的 :(NSArray *)moduleItemsList
追踪到作为 属性 持有它的任何人。然后覆盖 getter 以使用 dispatch_sync,并覆盖 setter 以使用 dispatch_barrier_async。但是,不能保证原始数组是任何 class 的 属性,其 getter 和 setter 可以被覆盖。实际上,none 这是有道理的,因为我们不会专门更改该数组,对吗?
我有点困惑。任何人都可以协助解决这个问题吗? #1 是我想要的选项吗?
编辑:添加更多代码
[item shouldCheckIFAnswered]
:
这只是检查 ModuleItem class 上存在的 @属性 值。 if self.moduleType == ModuleTypeSuchAndSuch
isAnsweredItem:
:
- (BOOL)isAnsweredItem:(NSString *)moduleID
{
if (!self.answeredItems) {
return NO;
}
return [self.answeredItems containsObject:moduleID];
}
从您的 post 看来,moduleItemsList
似乎在另一个线程中被修改了。 "correct" 解决此问题的方法将取决于其他线程中的状态与该线程中的状态之间的所需关系。
如果在这段代码中,以及在另一个线程中修改集合的代码中都使用@synchronized(moduleItemsList)
,那么当这段代码运行时,它会始终对 moduleItemsList
.
有 "up to date" 的看法
如果您将 moduleItemsList
复制到另一个对象中,那么当此代码运行时,它可能会在不再位于 moduleItemsList
中的项目上设置 answered
值,或者它可能无法为最近添加到 moduleItemsList
.
的项目设置 answered
标志
一般来说,@synchronized
版本更容易获得 "correct" 行为。如果您确定这两个线程可能不同意 moduleItemsList
.
的当前内容无关紧要,那么您只想使用复制
I've heard @synchronized is very slow and to avoid it when possible.
总的来说,这是糟糕的建议。 @synchronized
的速度与确保线程之间状态一致并提供可重入锁所需的速度一样慢。您不想随心所欲地对所有内容都使用 @synchronized
,但它是在线程之间同步数据访问的一个很好的解决方案 - 毕竟,这就是 for。
在 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 给出的错误可以在上面代码片段的注释中看到。
我想有几种方法可以解决这个问题。
1) 将函数内的所有内容包装在@synchronized(moduleItemsList) {} 中。这是解决问题的理想方法吗?我听说 @synchronized 非常慢,尽可能避免使用它。
2) 创建一个副本 NSMutableArray *copyModuleItemsList = [moduleItemsList mutableCopy];
。然后枚举那个。这会解决问题吗?我认为它会解决这个特定问题,但会有另一个问题不是吗?那是...最后,当我们将副本分配回原始 a la moduleItemsList = copyModuleItemsList;
时,moduleItemsList 可能同时在不同的线程上发生了变化。
3) 将传入的 :(NSArray *)moduleItemsList
追踪到作为 属性 持有它的任何人。然后覆盖 getter 以使用 dispatch_sync,并覆盖 setter 以使用 dispatch_barrier_async。但是,不能保证原始数组是任何 class 的 属性,其 getter 和 setter 可以被覆盖。实际上,none 这是有道理的,因为我们不会专门更改该数组,对吗?
我有点困惑。任何人都可以协助解决这个问题吗? #1 是我想要的选项吗?
编辑:添加更多代码
[item shouldCheckIFAnswered]
:
这只是检查 ModuleItem class 上存在的 @属性 值。 if self.moduleType == ModuleTypeSuchAndSuch
isAnsweredItem:
:
- (BOOL)isAnsweredItem:(NSString *)moduleID
{
if (!self.answeredItems) {
return NO;
}
return [self.answeredItems containsObject:moduleID];
}
从您的 post 看来,moduleItemsList
似乎在另一个线程中被修改了。 "correct" 解决此问题的方法将取决于其他线程中的状态与该线程中的状态之间的所需关系。
如果在这段代码中,以及在另一个线程中修改集合的代码中都使用@synchronized(moduleItemsList)
,那么当这段代码运行时,它会始终对 moduleItemsList
.
如果您将 moduleItemsList
复制到另一个对象中,那么当此代码运行时,它可能会在不再位于 moduleItemsList
中的项目上设置 answered
值,或者它可能无法为最近添加到 moduleItemsList
.
answered
标志
一般来说,@synchronized
版本更容易获得 "correct" 行为。如果您确定这两个线程可能不同意 moduleItemsList
.
I've heard @synchronized is very slow and to avoid it when possible.
总的来说,这是糟糕的建议。 @synchronized
的速度与确保线程之间状态一致并提供可重入锁所需的速度一样慢。您不想随心所欲地对所有内容都使用 @synchronized
,但它是在线程之间同步数据访问的一个很好的解决方案 - 毕竟,这就是 for。