1

ARC가 설정된 코코아 코드에서 아래의 창 닫기 이벤트를 관찰하려고했습니다. addObserverForName 및 옵저버 삭제

ScanWindowController * c = [[ScanWindowController alloc] initWithWindowNibName:@"ScanWindowController"]; 
[scanWindowControllers addObject:c]; 
[c showWindow:nil]; 

NSMutableArray *observer = [[NSMutableArray alloc] init]; 
observer[0] = [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:nil queue:nil usingBlock:^(NSNotification *note) { 
    [scanWindowControllers removeObject:c]; 
    [[NSNotificationCenter defaultCenter] removeObserver:observer[0]]; 
}]; 

제가 이 창을 닫은 후 상기 제어부 (C)에 대한 모든 참조를 제거 생각. 사실,이 코드는 Window를 닫은 후에 ScanWindowController를 dealloc하지 않습니다. 컨트롤러에 약한 참조를 사용하여 아래와 같이 작성하면 ScanWindowController의 dealloc이 호출됩니다.

ScanWindowController * c = [[ScanWindowController alloc] initWithWindowNibName:@"ScanWindowController"]; 
[scanWindowControllers addObject:c]; 
[c showWindow:nil]; 

__weak ScanWindowController * weak_c = c; 
NSMutableArray *observer = [[NSMutableArray alloc] init]; 
observer[0] = [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:nil queue:nil usingBlock:^(NSNotification *note) { 
    [scanWindowControllers removeObject:weak_c]; 
    [[NSNotificationCenter defaultCenter] removeObserver:observer[0]]; 
}]; 

왜 첫 번째 코드가 작동하지 않습니까?

답변

3

보유주기가 observer 어레이와 블록 사이에 있다고 생각합니다. observer 배열에는 실제 관찰자 개체가 있습니다. 관찰자 객체가 살아있는 한 그것은 블록을 유지합니다. 이 블록은 observer 어레이를 보유합니다.

이렇게하면 ScanViewController이 부작용으로 유지됩니다. 나는 ScanViewController이 관찰자에 대한 강한 참고 자료를 가지고 있다는 어떠한 증거도 발견하지 못했다.

해결책은 블록 끝의 observer 배열에서 관찰자를 제거하는 것입니다. 또 다른 해결책은 옵저버를 유지하기 위해 배열을 사용하지 않고 그냥 __block id 변수입니다. 그런 다음 해당 변수를 블록 끝의 nil으로 설정하십시오.

+0

** observer **는 지역 변수이며 ARC가 사용됩니다. 따라서 함수의 끝에서 ** observer ** 배열과 내용이 릴리즈되어야합니까? –

+1

ARC는 ** 해당 지역이 범위를 벗어날 때 지역 변수가 참조하는 객체가 파괴되지 않도록 ** 보장하지 않습니다 **. * 참조 중 하나가 해제되지만 객체가 할당 취소 된 것은 아닙니다. 블록은 다른 참조를 보유하고 있으며 관찰 객체 ('-addObserverForName : object : queue : usingBlock :'에 의해 반환 된 객체)가 존재하는 한 블록은 유지됩니다. 이 관찰 객체는 '관찰자'배열이 존재하는 한 존재합니다. 이것은 보존주기입니다. –

+0

아, 알 겠어! 그래서, ** arr = @ []; arr [0] = arr; **과 같이 썼습니다. 블록 끝에서 ** [observer removeAllObjects] **를 호출하면 참조 루프가 해결되었습니다. 감사 ! –