2013-04-02 1 views
3

나는 로그 정보를 보유하고 그것을 N 라인마다 파일에 덤프하는 logBuffer라는 NSMutableArray 객체를 가지고있다. 그렇게되면, 내가경계 초과 NSMutableArray removeAllObjects

[logBuffer removeAllObjects] 

로, 모든 항목을 제거 때때로 이것은 예외가 발생합니다 : 내가 배열에있는 모든 개체를 통해 removeAllObjects 내부적으로 반복 추측

[__NSArrayM removeObjectAtIndex:]: index 7 beyond bounds [0 .. 6] 

을, 그러나 나는 확실하지 않다 방법을 그 한계를 넘어 설 수 있습니다. 내 유일한 생각이지만 개체가 제거되는 동안 배열을 조작하는 다른 스레드가 있지만 전혀 모르겠습니다.

의견이 있으십니까?


편집 :

- (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 
} 

내가 테스트 fe에 대한 코드 w 시간이 걸리며 예외가 발생하지 않았습니다. 그것은 시간당 약 1-2 번 충돌하기 때문에 문제가 해결되었다고 가정합니다. 누군가이 접근법이 @synchronize 제안과 어떻게 다른지 설명 할 수 있습니까?

또한 waitUntilDone : YES를 사용하는 것이 좋을지, 아니면이 경우 NO를 사용하는 것이 더 좋을까요? 당신은 그것을 제공 한

+2

"내 유일한하지만 개체가 제거되는 동안 배열을 조작하는 다른 스레드 있다는 것입니다"- 경쟁 조건 내 순간이었다가 생각뿐. 어떻게 배열을 사용하고 있습니까? –

+0

* removeAllObjects *를 호출하기 바로 전에 배열의 크기를 기록하십시오. 그런 다음 충돌 로그에서 배열의 크기를 읽습니다 (예를 들어, [0..6]은 크기가 7입니다). 크기가 변경되면 * removeAllObjects * 실행 중에 배열을 편집하고있는 것입니다. – rdurand

+0

개체를 반복적으로 제거하고 시도해 볼 수 있습니다. 아마도 무언가를 찾을 수 있습니다. – rdurand

답변

2

비록 정보가 ... 무슨 잘못이야 그러나 저에 따라 문제가 주 스레드 또는 다른 스레드를 사용하여를 반복하는 동안 배열을 수정 될 수 있음을 수 말해 어렵다

+0

배열 멤버의 dealloc 메서드가 배열을 수정하는 경우이 문제가 발생할 수 있습니다. –

4

사용 @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를 호출하면

, 나는이 두 가지 중 하나를 수행합니다 : : 의견 및 편집 질문 고려 계속에서

,

  1. addToLogwriteLogOnFile 코드를 병합을, 이후 어쨌든 1 : 1입니다. 이렇게하면 아무 것도 writeLogOnFile에 직접 전화 할 수 없습니다.이 경우 addToLog을 완전히 @synchronized(logBuffer) {} 내에 넣으십시오.

  2. writeLogOnFile을 어떤 이유로 든 분리하려는 경우이 방법을 클래스에 비공개로 설정하십시오. 두 경우 모두, 이론적으로 당신이 클래스 내에서 무엇을하고 있는지 알고 있기 때문에이 경우 writeLogOnFile@synchronized(logBuffer) 제거 할 수 있지만, 당신이 볼 수 있듯이 당신은 또한 @synchronized(logBuffer) {}

내에서 완전히 addToLog 포장해야 당신 addToLog@synchronized을 통해 완전히 단일 스레드로 만들어야합니다 (원래 응답을 유지해야 함). 매우 간단하고 코드를 깨끗하게 유지하며 편집 된 질문이 해결하려고하는 모든 스레딩 문제를 제거합니다. @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]; 
     } 
    } 
} 
+0

대신 @synchronized (logBuffer) {NSArray * bufferCopy = ...; [logBuffer removeAllObjects]; }'writeLogOnFile'의 시작 부분에. 이것은 로그 항목이 배열 복제와 객체 제거 사이에 추가 될 가능성을 방지합니다. 추가 된 모든 객체는 파일에 기록되지 않지만'logBuffer'가 지워질 때 제거됩니다. – bdesham

+0

실제로 쓰기를 입력하는 유일한 방법이 (동기화 된) addToLog를 통해 이루어지는 경우 두 번째 @syncronized는 필요하지 않습니다. 그러나 이것은 내가 할 수있는 방법이 아닙니다. 나는 생각하지 않습니다. –

+0

내 답변은 실제로 할 수있는 일의 샘플입니다. @dimitrios가 여러 위치에서'[logBuffer addObject : s]'를 호출하고 있으며,'[self writeLogOnFile]'과 비슷하게 가정합니다. '@synchronized'를 사용하면 매우 유연하게 호출 할 수 있습니다. – Rikkles

관련 문제