仅在发布模式下“枚举 NSArray 集合时发生突变”

Posted

技术标签:

【中文标题】仅在发布模式下“枚举 NSArray 集合时发生突变”【英文标题】:"Collection NSArray was mutated while being enumerated" in Release Mode only 【发布时间】:2014-12-17 14:27:08 【问题描述】:

我收到"Collection NSArray was mutated while being enumerated" 异常,仅在发布模式下。当我在调试模式下运行应用程序时,一切正常。

如果发布模式下的调试器是准确的,它会在这行代码上崩溃:

- (void)initPersonsTableTableColumnMetadata:(CBEditPersonTableColumnMetadata *)metadata count:(NSInteger)valuesCount

    personsTableColumnMetadata = [[NSMutableDictionary alloc] initWithCapacity:valuesCount];
    NSAutoreleasePool *metadataPool = [[NSAutoreleasePool alloc] init];
    for (NSUInteger i = 0; i < valuesCount; i++) 
        CBEditPersonTableColumnMetadata aMetadata = metadata[i];
        NSValue *metadataValue = [NSValue valueWithBytes:&aMetadata objCType:@encode(CBTableColumnMetadata)];
        [personsTableColumnMetadata setObject:metadataValue forKey:aMetadata.columnKey];
    
    FreeAndNil(metadataPool);



-(CBEditPersonTableColumnMetadata)tableColumnMetadataForKey:(NSString *)aKey

    CBEditPersonTableColumnMetadata theMetadata;
    NSValue *storedValue = [personsTableColumnMetadata valueForKey:aKey];
    if (storedValue) 
        [storedValue getValue:&theMetadata]; //here
        return theMetadata;
    


- (void)addColumnsToPersonTable:(CBEditableTableView *)table withKeys:(NSArray *)keys andAutosaveName:(NSString*)autosaveName

    for (NSString *columnKey in keys) 
        CBEditPersonTableColumnMetadata metadata = [self tableColumnMetadataForKey:columnKey];
        NSTableColumn *newColumn = [[NSTableColumn alloc] initWithIdentifier:columnKey];

        NSCell *cell = nil;

        if ([metadata.columnKey isEqualToString:@"Dragging"]) 
            cell = [[NSImageCell alloc] init];

         else if ([metadata.editType isEqualToString:[self editableButton]]) 
            cell = [[NSButtonCell alloc] init];
            [cell setAction:@selector(selectAndOpenEditLookup:)];
            [cell setTag:[metadata.editEntity intValue]];
            [cell setImage:[NSImage imageNamed:@"edit-inlist-16x16.png"]];
            [(NSButtonCell*)cell setImageScaling:NSImageScaleProportionallyDown];
            [cell setTarget:self];
            [cell setTitle:@""];

         else 
            cell = [[CBPaddedTextCell alloc] init];
            if ([metadata.editType isEqualToString:[self notEditableComboBox]]) 
                [cell setEditable:NO];
            
        

        [newColumn setResizingMask:NSTableColumnUserResizingMask];
        [[newColumn headerCell] setStringValue:[NSString stringWithFormat:@"  %@",metadata.columnHeader]];

        [[newColumn headerCell] setLineBreakMode:NSLineBreakByTruncatingTail]; // or NSLineBreakByTruncatingMiddle

        if (metadata.minWidth > 0.0) 
            [newColumn setMinWidth:metadata.minWidth];
        
        if (metadata.maxWidth > 0.0) 
            [newColumn setMaxWidth:metadata.maxWidth];
        

        [newColumn setWidth:100.0];

        if (![metadata.editType length])
            if ([cell isKindOfClass:[NSTextFieldCell class]])
                [(NSTextFieldCell*)cell setTextColor:[NSColor grayColor]];

        [newColumn setDataCell:cell];
        FreeAndNil(cell);

        if (![metadata.editType isEqualToString:[self editableButton]]) 
            NSDictionary *bindingOptions =  [NSDictionary dictionaryWithObjectsAndKeys:
                                            [NSNumber numberWithBool:NO], NSCreatesSortDescriptorBindingOption,
                                            [NSNumber numberWithBool:YES], NSEditableBinding,
                                            nil];

            [newColumn bind:@"value" toObject:[table arrayController] withKeyPath:[NSString stringWithFormat:@"arrangedObjects.%@", metadata.bindingKeyPath] options:bindingOptions];
            [[newColumn dataCell] setEditable:YES];
        

        [newColumn setIdentifier:columnKey];
        [table addTableColumn:newColumn];
        FreeAndNil(newColumn);
    

    NSTableColumn *newColumn = [[NSTableColumn alloc] initWithIdentifier:@"Remove"];

    NSButtonCell *cell = [[NSButtonCell alloc] init];
    [cell setAction:@selector(removeEditableItem:)];
    [cell setImage:[NSImage imageNamed:@"remove.png"]];
    [cell setImageScaling:NSImageScaleProportionallyDown];
    [cell setTarget:self];
    [cell setTitle:@""];
    [newColumn setMinWidth:20];
    [newColumn setMaxWidth:20];
    [newColumn setWidth:20.0];
    [[newColumn headerCell] setStringValue:@""];

    [newColumn setDataCell:cell];
    FreeAndNil(cell);
    [table addTableColumn:newColumn];
    FreeAndNil(newColumn);

    [table setNeedsDisplay];
    [table reloadData];

    [table setAutosaveName:autosaveName];
    [table setAutosaveTableColumns:YES];


- (void)windowDidLoad

    [super windowDidLoad];


    NSArray * artistKeys = [NSArray arrayWithObjects:@"Dragging", @"DisplayName", @"FirstName", @"LastName", @"SortName", @"ID", nil];
    [editableArtistTable setArrayController:artistsArrayController];
    [self addColumnsToPersonTable:editableArtistTable withKeys:artistKeys andAutosaveName:@"editableArtistTable"];

CBEditPersonTableColumnMetadata

typedef struct _CBEditPersonTableColumnMetadata 
    NSString *columnKey;
    NSString *columnHeader;
    NSString *bindingKeyPath;
    CGFloat minWidth;
    CGFloat maxWidth;
    NSString *editType;
    NSString *editEntity;
    BOOL     forbidEmpty;
 CBEditPersonTableColumnMetadata;

不知道为什么。调试模式一切正常。

更新:控制台日志:

2014-12-17 16:13:20.580 *** Collection <__NSArrayI: 0xf60f0a0> was mutated while being enumerated.
2014-12-17 16:13:20.582 Colr (
    0   CoreFoundation                      0x9711a343 __raiseError + 195
    1   libobjc.A.dylib                     0x98bbda2a objc_exception_throw + 276
    2   CoreFoundation                      0x97119c0a __NSFastEnumerationMutationHandler + 362
    3   Colr                     0x00165e16 -[CBEditItemWithOutletsBase addColumnsToPersonTable:withKeys:andAutosaveName:] + 149
    4   Colr                     0x00192413 -[CBEditItemWithOutlets windowDidLoad] + 343

【问题讨论】:

【参考方案1】:

好吧,经过一番调查(你可以看到上面的 cmets)原来问题出在initPersonsTableTableColumnMetadata:count: 方法

下面一行:

NSValue *metadataValue = [NSValue valueWithBytes:&aMetadata objCType:@encode(CBTableColumnMetadata)];  

应该是:

NSValue *metadataValue = [NSValue valueWithBytes:&aMetadata objCType:@encode(CBEditPersonTableColumnMetadata)];  

@encode 结构类型不正确。 因此,在尝试将其解码回 CBEditPersonTableColumnMetadata 结构时它崩溃了。

我仍然有点困惑为什么异常表明它是由于在枚举它时改变了数组,但崩溃的方法是从枚举中调用的(即使它对数组的对象没有影响),并修复上述问题解决了崩溃问题。

【讨论】:

以上是关于仅在发布模式下“枚举 NSArray 集合时发生突变”的主要内容,如果未能解决你的问题,请参考以下文章

父 NSManagedObjectContext 在子保存后没有变化,但仅在发布模式下(在调试模式下工作)

iPhone 应用程序仅在 3G 上的发布模式下崩溃

Firebase 消息传递仅在调试模式下工作,而不是在发布模式下工作 (iOS)

程序仅在调试器外的发布模式下崩溃

仅在调试模式下生成的 C4715

如何仅在调试模式下显示小部件 [重复]