【问题标题】:Unusual case of "collection was mutated..." while processing background thread处理后台线程时出现“集合发生变异...”的异常情况
【发布时间】:2014-03-26 17:50:56
【问题描述】:

我正在尝试使用完成处理程序从后台线程上的文本文件加载数据,该完成处理程序将在我使用正则表达式处理数据后写出一个 plist。我收到“枚举时集合发生突变”错误,但我不确定为什么在这种情况下会抛出它。我正在使用块和后台线程。所以我假设我犯了一个逻辑错误,但我不明白为什么:

  1. 可变数组的计数在断点处不一致(发生错误的地方)
  2. 在某一时刻,该过程确实正确完成(填充了 data.plist 文件)
  3. 当我在归档之前尝试使用可变数组的副本时,我 收到“尝试插入 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 包含可变数量的对象(每个周期中的对象数量不同,这使我相信我的后台线程尚未完成处理)。

【问题讨论】:

    标签: objective-c multithreading nsmutablearray objective-c-blocks grand-central-dispatch


    【解决方案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,执行|想知道我的后台任务有时是否未完成?
    • 我最终使用了这种方法,我确定我的其他问题与线程有关。
    猜你喜欢
    • 2011-02-09
    • 2014-08-07
    • 2011-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多