使用[nsdictionary enumerateKeysAndObjectsUsingBlock:]时,我得到了一个双自由错误
CnFExhibition_0821(74624,0x114853000)malloc:*对象0x7fe972814fa0错误:双自由
这种情况主要发生在使用appendFormat时:在枚举块中,并不总是发生,但经常发生。
我最终通过不使用enumerateKeysAndObjectsUsingBlock:来阻止它,但仍然想知道为什么?
当调用这行时会发生这种情况,"newData.list[0]"是一个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;
}
}
用下面的代码替换"sqlCmdSetRow"中的枚举,并且不再发生
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]];
}
}
通过将-[NSDictionary enumerateKeysAndObjectsWithOptions:usingBlock:
与NSEnumerationConcurrent
选项一起使用,可以有效地在多个线程上同时调用同一NSMutableString
对象(setCmd
)上的appendString:
和appendFormat:
。这些方法的文档中没有任何关于线程安全的内容,所以它们可能不是线程安全的。你的随机崩溃支持了这一点。
更改为for-in
循环是正确的。现在您只在单个线程上接触setCmd
,就不存在线程安全问题,崩溃也就消失了。