Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/105.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios NSMutableArray RemoveAllObject超出边界异常_Ios_Objective C_Exception_Nsmutablearray - Fatal编程技术网

Ios NSMutableArray RemoveAllObject超出边界异常

Ios NSMutableArray RemoveAllObject超出边界异常,ios,objective-c,exception,nsmutablearray,Ios,Objective C,Exception,Nsmutablearray,我有一个名为logBuffer的NSMutableArray对象,它保存日志信息并每隔N行将其转储到一个文件中。当这种情况发生时,我将删除它的所有条目,并使用 [logBuffer removeAllObjects] 有时这会引发异常: [__NSArrayM removeObjectAtIndex:]: index 7 beyond bounds [0 .. 6] 我猜removeAllObjects在内部会遍历数组中的所有对象,但我不确定它如何超越其边界。我唯一的想法是,在移除对象时,有

我有一个名为logBuffer的NSMutableArray对象,它保存日志信息并每隔N行将其转储到一个文件中。当这种情况发生时,我将删除它的所有条目,并使用

[logBuffer removeAllObjects]
有时这会引发异常:

[__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
}
我对代码进行了几个小时的测试,它没有抛出异常。它过去每小时崩溃1-2次,所以我认为问题已经解决了。有人能解释一下这种方法与
@synchronize
建议有何不同吗


另外,使用waitUntilDone是否明智:在这种情况下,是或否会更好?

虽然您提供的信息很难判断出哪里出了问题,但根据我的说法,问题可能是您可能在使用主线程或其他线程迭代数组时修改了数组。

使用
@synchronized(日志缓冲区)

编辑:因为我使用的是
@synchronized
,所以我们可以去掉缓冲区副本,只需同步

继续,考虑到评论和编辑的问题:

如果您仅从
addToLog
调用
writeLogOnFile
,那么我将执行以下两种操作之一:

  • writeLogOnFile
    代码合并到
    addToLog
    ,因为它是1对1。这确保没有任何东西会直接调用
    writeLogOnFile
    。在这种情况下,将
    addToLog
    完全包装在
    @synchronized(logBuffer){}

  • 如果出于任何原因希望将
    writeLogOnFile
    分开,则将此方法设为类的私有方法。在这种情况下,可以去掉
    @synchronized(logBuffer)
    writeLogOnFile
    中,因为理论上你知道你在类中做什么,但是你也应该将
    addToLog
    完全包装在
    @synchronized(logBuffer){}

  • 正如您所看到的,在这两种情况下,您都应该完全通过
    @synchronized
    (或保留原始答案)使
    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];
          }
        }
    }
    

    “我唯一的想法是,当对象被移除时,有另一个线程操纵数组”-竞争条件也是我立即想到的。您如何使用数组?在调用removeAllObjects之前,尝试记录数组的大小。然后在崩溃日志中,读取数组的大小(在您的示例中,[0..6]给出了一个7)的大小。如果大小更改,您可能正在执行removeAllObjects期间编辑数组。您可以尝试手动迭代和删除对象,也许您会发现一些问题。请记住,如果由于存储错误而进行此崩溃日志记录,堆可能已损坏,并且所有赌注都已取消。如果从多线程时,您有几个严重的争用情况。至少您不应该重用阵列,而是每次创建一个新的阵列(除了一些同步)因此,如果在写入过程中发生新的日志操作,这两个操作将不会发生干扰。可能发生这种情况的一种方法是,如果数组成员的dealloc方法修改了arrayI,则改为执行
    @synchronized(logBuffer){NSArray*bufferCopy=…;[logBuffer removeAllObjects];}
    writeLogOnFile
    的开始处。这可以防止在复制数组和删除对象之间添加日志项的可能性——任何添加的对象都不会写入文件,而是在清除
    logBuffer
    时删除。实际上,如果打开,则不需要第二个@syncronized输入write…的唯一方法是通过(同步的)addToLog。但我认为这不是我的方法。我的答案实际上是一个可以完成的示例。我假设@dimitrios可能从许多地方调用
    [logBuffer addObject:s]
    ,类似的还有
    [self writegonfile]
    。使用
    @synchronized
    可以非常灵活地选择如何和何时调用此功能。@Rikkles感谢您的建议。我正在呼叫[CrashManager addToLog:]来自许多地方,但除了addToLog:和writeLogOnFile方法内部之外,没有直接调用logBuffer。我再次编辑了我的原始问题,我想了解您的建议是否比我实现的建议更可取。
    - (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];
          }
        }
    }