NSMutableArray removeAllObjects超出边界exception
我有一个名为logBuffer的NSMutableArray对象,它保存日志信息并将其每隔N行转储到一个文件中。 当发生这种情况时,我删除所有条目
[logBuffer removeAllObjects]
有时会引发exception:
[__NSArrayM removeObjectAtIndex:]: index 7 beyond bounds [0 .. 6]
我想removeAllObjects在内部迭代遍历数组中的所有对象,但我不确定它是如何超越它的界限的。 我唯一的问题是在删除对象时有另一个操作数组的线程,但我根本不确定。
有什么想法吗?
编辑:这是一些额外的代码:
- (void) addToLog:(NSString*)str { [logBuffer addObject:s]; if ([logBuffer count] >= kBufferSize) { [self writeLogOnFile]; } } - (void) writeLogOnFile { NSArray *bufferCopy = [NSArray arrayWithArray:logBuffer]; // create a clone, so that logBuffer doesn't change while dumping data and we have a conflict NSString *multiline = [bufferCopy componentsJoinedByString:@"\r\n"]; multiline = [NSString stringWithFormat:@"%@\n", multiline]; NSData *data = [multiline dataUsingEncoding:NSUTF8StringEncoding]; NSFileHandle *outputFileHandle = [NSFileHandle fileHandleForWritingAtPath:logFilePath]; [outputFileHandle seekToEndOfFile]; [outputFileHandle writeData:data]; [outputFileHandle closeFile]; [logBuffer removeAllObjects]; // This is where the exception is thrown }
[CrashManager addToLog:]由几十个类调用,并不总是在主线程上。
这是回溯:
"0 AClockworkBrain 0x0008058f -[SWCrashManager backtrace] + 79", "1 AClockworkBrain 0x0007fab6 uncaughtExceptionHandler + 310", "2 CoreFoundation 0x041fe318 __handleUncaughtException + 728", "3 libobjc.A.dylib 0x03c010b9 _ZL15_objc_terminatev + 86", "4 libc++abi.dylib 0x044c9a65 _ZL19safe_handler_callerPFvvE + 13", "5 libc++abi.dylib 0x044c9acd __cxa_bad_typeid + 0", "6 libc++abi.dylib 0x044cabc2 _ZL23__gxx_exception_cleanup19_Unwind_Reason_CodeP17_Unwind_Exception + 0", "7 libobjc.A.dylib 0x03c00f89 _ZL26_objc_exception_destructorPv + 0", "8 CoreFoundation 0x041171c4 -[__NSArrayM removeObjectAtIndex:] + 212", "9 CoreFoundation 0x04153f70 -[NSMutableArray removeAllObjects] + 96", "10 AClockworkBrain 0x000817c3 -[SWCrashManager writeLogOnFile] + 691", "11 AClockworkBrain 0x0008141d -[SWCrashManager addToLog:] + 429", *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM removeObjectAtIndex:]: index 7 beyond bounds [0 .. 6]'
编辑#2
在阅读有关@synchronize
的建议后,我将其修改为:
- (void) addToLog:(NSString*)str { [self performSelectorOnMainThread:@selector(doAddToLog:) withObject:str waitUntilDone:YES]; } - (void) doAddToLog:(NSString*)str { // Do the real stuff } - (void) writeLogOnFile { [self performSelectorOnMainThread:@selector(doWriteLogOnFile) withObject:nil waitUntilDone:YES]; } - (void) doWriteLogOnFile { // Do the real stuff }
我测试了几个小时的代码,并没有抛出exception。 它曾经每小时崩溃大约1-2次,所以我认为问题是固定的。 有人可以解释这种方法与@synchronize
建议的区别吗?
另外,使用waitUntilDone是明智的:YES或者在这种情况下NO会更好吗?
使用@synchronized(logBuffer)
:
- (void) addToLog:(NSString*)str { @synchronized(logBuffer) { [logBuffer addObject:s]; } if ([logBuffer count] >= kBufferSize) { [self writeLogOnFile]; } } - (void) writeLogOnFile { @synchronized(logBuffer) { NSString *multiline = [logBuffer componentsJoinedByString:@"\r\n"]; multiline = [NSString stringWithFormat:@"%@\n", multiline]; NSData *data = [multiline dataUsingEncoding:NSUTF8StringEncoding]; NSFileHandle *outputFileHandle = [NSFileHandle fileHandleForWritingAtPath:logFilePath]; [outputFileHandle seekToEndOfFile]; [outputFileHandle writeData:data]; [outputFileHandle closeFile]; [logBuffer removeAllObjects]; // This is where the exception is thrown } }
编辑:由于我正在使用@synchronized
,我们可以摆脱缓冲区副本,只需同步。
继续,考虑评论和编辑问题:
如果你只从addToLog
调用writeLogOnFile
,那么我会做两件事之一:
-
将
writeLogOnFile
代码合并到addToLog
,因为它无论如何都是1比1。 这确保了不会直接调用writeLogOnFile
。 在这种情况下,将addToLog
完全包装在addToLog
@synchronized(logBuffer) {}
-
如果你想将
writeLogOnFile
分开,无论出于何种原因,那么将这个方法设为私有。 在这种情况下,你可以在writeLogOnFile
@synchronized(logBuffer)
,因为理论上你知道你在类中做了什么,但你也应该将addToLog
完全包装在addToLog
@synchronized(logBuffer) {}
正如您所看到的,在这两种情况下,您绝对应该通过addToLog
使addToLog
完全单线程(或保留原始答案)。 它非常简单,可以保持代码清洁,并消除您编辑的问题试图解决的所有线程问题。 @synchronized
模式是专门创建的,以避免编写您编写的所有包装代码来解决您的问题,即强制所有内容通过主线程(或特定线程)。
为了完整起见,这里是我要编写的完整代码:
- (void) addToLog:(NSString*)str { @synchronized(logBuffer) { [logBuffer addObject:s]; if ([logBuffer count] >= kBufferSize) { // write log to file NSString *multiline = [logBuffer componentsJoinedByString:@"\r\n"]; multiline = [NSString stringWithFormat:@"%@\n", multiline]; NSData *data = [multiline dataUsingEncoding:NSUTF8StringEncoding]; NSFileHandle *outputFileHandle = [NSFileHandle fileHandleForWritingAtPath:logFilePath]; [outputFileHandle seekToEndOfFile]; [outputFileHandle writeData:data]; [outputFileHandle closeFile]; [logBuffer removeAllObjects]; } } }
虽然您提供的信息很难说出错是什么但是根据我的问题可能是您可能正在修改数组,同时使用主线程或其他线程迭代它。