Search code examples
iosobjective-cautomatic-ref-countingnsdictionary

NSDictionary Enumeration Cause "malloc: double free" Error


I get a double free error when using [nsdictionary enumerateKeysAndObjectsUsingBlock:]

CnFExhibition_0821(74624,0x114853000) malloc: * error for object 0x7fe972814fa0: double free

This happen mostly when using appendFormat: in enumeration block, not always happen but quite often.

I finally prevent it by not using enumerateKeysAndObjectsUsingBlock:, but still wondering why?

This happen when calling this line, which "newData.list[0]" is a NSDictionary,

[database updateRow:@"01" inTable:@"Exhibition" setting:newData.list[0] error:nil];

SQLiteHelper.m

-(BOOL)updateRow:(id)rowID inTable:(NSString*)tablename setting:(NSDictionary*)setting error:(NSError *__autoreleasing *)err{
    if ([self checkTableName:tablename error:err]){
        if ([self checkUpdateSetting:setting error:err]) {
            NSMutableString * updateCmd = [sqlCmdUpdateFromTable(tablename) mutableCopy];
            [updateCmd appendString:sqlCmdSetRow(setting)];
            if (rowID) {
                [updateCmd appendString:sqlCmdWhereCondition(@{[self pkOfTable:tablename]:rowID})];
            }
            return [self execCmdStr:updateCmd error:err];
        }else{
        return NO;
        }
    }else{
        return NO;
    }
}

NSString * sqlCmdSetRow(NSDictionary*setting){
    if (setting && setting.count){
        NSMutableString * setCmd = [NSMutableString stringWithString: @" SET "];     
        [[setting copy] enumerateKeysAndObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id key, id obj, BOOL *stop){
         if ([obj isKindOfClass:[NSString class]]){

//*Mostly crush at this line*
             [setCmd appendFormat:@"%@='%@', ",key,obj]];
         }
         else{
             [setCmd appendFormat:@"%@=%@, ",key,obj];
         }
     }];
    [setCmd deleteCharactersInRange:NSMakeRange(setCmd.length-2, 2)];
    return setCmd;
}
else{
    return nil;
}

}

Replacing enumeration in "sqlCmdSetRow" with code below and never happen again

NSArray * a = [setting allKeys];
for (NSString * s in a) {
    if ([setting[s] isKindOfClass:[NSString class]]){
        [setCmd appendString:[NSString stringWithFormat:@"%@='%@', ",s,setting[s]]];
    }else{
        [setCmd appendFormat:@"%@=%@, ",s,setting[s]];
    }            
}

Solution

  • By using -[NSDictionary enumerateKeysAndObjectsWithOptions:usingBlock: with the NSEnumerationConcurrent option, you are effectively calling appendString: and appendFormat: on the same NSMutableString object (setCmd) on multiple threads simultaneously. The documentation of those methods doesn't say anything about thread safety, so they probably aren't thread-safe. Your random crashes back that up.

    You did the right thing by changing to a for-in loop. Now that you are only touching setCmd on a single thread, there is no thread safety issue and the crashes went away.