处理后台线程时出现“集合发生变异...”的异常情况
Posted
技术标签:
【中文标题】处理后台线程时出现“集合发生变异...”的异常情况【英文标题】:Unusual case of "collection was mutated..." while processing background thread 【发布时间】:2014-03-26 17:50:56 【问题描述】:我正在尝试使用完成处理程序从后台线程上的文本文件加载数据,该完成处理程序将在我使用正则表达式处理数据后写出一个 plist。我收到“枚举时集合发生突变”错误,但我不确定为什么在这种情况下会抛出它。我正在使用块和后台线程。所以我假设我犯了一个逻辑错误,但我不明白为什么:
-
可变数组的计数在断点处不一致(发生错误的地方)
在某一时刻,该过程确实正确完成(填充了 data.plist 文件)
当我在归档之前尝试使用可变数组的副本时,我
收到“尝试插入 nil 对象”错误,但此时原始 nsmutablearray 计数似乎有效(请参阅更新)
有趣的是,由于计数不一致和(在某一时刻)成功完成,我认为我的后台处理可能是罪魁祸首。
下面是 DataManager 类的 init 方法的一部分:
if ( !abcMutableArray )
abcMutableArray = [[NSMutableArray alloc] init];
NSFileManager *fileManager = [NSFileManager defaultManager];
// If the plist file doesn't exist in the Documents Folder, create it from the text file
if ( ![fileManager fileExistsAtPath:[self filePath]] )
[self createDataFile];
这里是辅助方法
- (void) createDataFile
__weak DataManager* weakSelf = self;
[weakSelf loadData:^(BOOL completed)
if(completed)
//Next line produces **Collection <__NSArrayM: 0x8fa28c0> was mutated while being enumerated**
NSData* data = [NSKeyedArchiver archivedDataWithRootObject:[weakSelf abcMutableArray]];
[data writeToFile:[weakSelf filePath] atomically:YES];
];
此方法执行加载,完成后将完成调用传回给调用者
- (void) loadData:(void (^)(BOOL))completed
__weak DataManager* weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^
NSError *localError = NULL;
NSStringEncoding fileEncoding = NSUTF8StringEncoding;
NSURL* fileURL = [[NSBundle mainBundle] URLForResource:@"data" withExtension:@"txt"];
NSString* fh= [NSString stringWithContentsOfURL:fileURL usedEncoding:&fileEncoding error:&localError];
for (NSString *line in [fh componentsSeparatedByString:@"\n"])
if ( ![line hasPrefix:@"#"] )
// [self parseLine:line];
//parsing routine
NSError *error = NULL;
Thing* t = [[Thing alloc]init];
NSString* pattern = @"(^.*)\\s(.*)\\s\\[(.*)\\]\\s\\/(.*)\\;\\s(.*)";
NSRegularExpression *regEx = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];
NSArray *matches = [regEx matchesInString:line
options:0
range:NSMakeRange(0, [line length])];
for (NSTextCheckingResult *match in matches)
if ([match numberOfRanges] == 6)
//The range at index 0 contains the entire string.
thing.a = (NSString*)[line substringWithRange:[match rangeAtIndex:1]];
thing.b = (NSString*)[line substringWithRange:[match rangeAtIndex:2]];
thing.c = (NSString*)[line substringWithRange:[match rangeAtIndex:3]];
thing.d = (NSString*)[line substringWithRange:[match rangeAtIndex:4]];
thing.e = (NSString*)[[[line substringWithRange:[match rangeAtIndex:5]]stringByReplacingOccurrencesOfString:@"/" withString:@". "] stringByReplacingOccurrencesOfString:@".." withString:@"."];
[[weakSelf abcMutableArray] addObject:thing];
//end-if match
//end-for matches
//end-if line
//end-for lines
//file load data process is completed
completed(YES);
);
更新
尝试按照建议将复制方法添加到代码中时:
NSData* data = [NSKeyedArchiver archivedDataWithRootObject:[[weakSelf abcMutableArray]copy]];
我在同一断点处收到以下错误。
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[12]'
请注意,当我在上面的 NSData 行中断时,我可以看到 abcMutableArray 包含可变数量的对象(每个周期中的对象数量不同,这使我相信我的后台线程尚未完成处理)。
【问题讨论】:
【参考方案1】:变化:
NSData* data = [NSKeyedArchiver archivedDataWithRootObject:[weakSelf abcMutableArray]];
收件人:
NSData* data = [NSKeyedArchiver archivedDataWithRootObject:[[weakSelf abcMutableArray] copy]];
这将传入数组的副本,因此不可能对其进行变异。
【讨论】:
好建议,但是,当反复测试时,有时会抛出 *** Terminating app due to unaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderArray initWithObjects:count:]: 尝试插入来自对象[2443]'的零对象' |我的测试步骤是执行,见data.plist,删除data.plist,执行|想知道我的后台任务有时是否未完成? 我最终使用了这种方法,我确定我的其他问题与线程有关。以上是关于处理后台线程时出现“集合发生变异...”的异常情况的主要内容,如果未能解决你的问题,请参考以下文章
对象转换json对象中存在Date,json转换对象时出现转换异常情况
对象转换json对象中存在Date,json转换对象时出现转换异常情况