짧은 답변
self
직접 액세스하는 대신 유지되지 않는 참조에서 간접적으로 액세스해야합니다. ARC (Automatic Reference Counting)를 사용하지 않는 경우 다음 을 수행 할 수 있습니다.
__block MyDataProcessor *dp = self;
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}
__block
블록 내에서 수정 될 수 있습니다 키워드 마크 변수 (우리는 그렇게하지 않을)하지만 (당신은 ARC를 사용하지 않는 경우) 또한 자동으로 블록이 유지 될 때 유지되지 않습니다. 이 작업을 수행하면 MyDataProcessor 인스턴스가 릴리스 된 후 다른 어떤 것도 블록을 실행하려고하지 않아야합니다. (코드 구조에 문제가 없어야합니다.) 자세한 내용을 읽으십시오__block
.
ARC를 사용하는 경우__block
변경 의미 및 참조 의 의미 가 유지되므로 __weak
대신 선언해야 합니다.
긴 대답
다음과 같은 코드가 있다고 가정 해 봅시다.
self.progressBlock = ^(CGFloat percentComplete) {
[self.delegate processingWithProgress:percentComplete];
}
여기서 문제는 자아가 블록에 대한 참조를 유지한다는 것입니다. 한편 블록은 델리게이트 속성을 가져오고 델리게이트에 메소드를 보내려면 self에 대한 참조를 유지해야합니다. 앱의 다른 모든 객체가이 객체에 대한 참조를 해제하면, 블록이 객체를 가리 키기 때문에 유지 횟수가 0이 아니고 (객체가 객체를 가리 키기 때문에) 블록이 잘못 수행하지 않으므로 객체 쌍은 힙으로 누출되어 메모리를 차지하지만 디버거 없이는 도달 할 수 없습니다. 정말 비극적입니다.
대신이 작업을 수행하면이 문제를 쉽게 해결할 수 있습니다.
id progressDelegate = self.delegate;
self.progressBlock = ^(CGFloat percentComplete) {
[progressDelegate processingWithProgress:percentComplete];
}
이 코드에서, self는 블록을 유지하고, 블록은 델리게이트를 유지하며,주기는 없습니다 (여기에서 볼 수 있습니다. 델리게이트는 객체를 유지할 수는 있지만 지금은 손에서 벗어났습니다). 이 코드는 동일한 방식으로 누수를 발생시키지 않습니다. 델리게이트 속성의 값은 블록이 생성 될 때 조회되지 않고 블록이 생성 될 때 캡처되기 때문입니다. 부작용은이 블록을 생성 한 후 델리게이트를 변경해도 블록이 여전히 기존 델리게이트로 업데이트 메시지를 전송한다는 것입니다. 그 가능성이 있는지 여부는 응용 프로그램에 따라 다릅니다.
당신이 그 행동에 멋지더라도, 당신의 경우에는 여전히 그 트릭을 사용할 수 없습니다 :
self.dataProcessor.progress = ^(CGFloat percentComplete) {
[self.delegate myAPI:self isProcessingWithProgress:percentComplete];
};
여기 self
에서는 메소드 호출에서 대리자로 직접 전달 되므로 어딘가에 가져와야합니다. 블록 유형의 정의를 제어 할 수있는 가장 좋은 방법은 대리자를 매개 변수로 블록에 전달하는 것입니다.
self.dataProcessor.progress = ^(MyDataProcessor *dp, CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
};
이 솔루션은 사이클을 유지 방지 하고 항상 현재 대리자를 호출합니다.
블록을 변경할 수 없으면 처리 할 수 있습니다 . 유지주기가 오류가 아닌 경고 인 이유는 응용 프로그램에 대해 반드시 명예를 훼손하지는 않기 때문입니다. MyDataProcessor
작업이 완료 될 때 블록을 해제 할 수 있으면 부모가 해제하려고 시도하기 전에주기가 중단되고 모든 것이 올바르게 정리됩니다. 이것을 확신 할 수 있다면, 올바른 #pragma
코드 블록에 대한 경고를 억제 하기 위해 a 를 사용하는 것이 옳습니다 . (또는 파일 별 컴파일러 플래그를 사용하십시오. 그러나 전체 프로젝트에 대해 경고를 비활성화하지 마십시오.)
위의 비슷한 트릭을 사용하여 참조를 약하거나 유지하지 않고 블록에서 사용하는 것을 볼 수도 있습니다. 예를 들면 다음과 같습니다.
__weak MyDataProcessor *dp = self; // OK for iOS 5 only
__unsafe_unretained MyDataProcessor *dp = self; // OK for iOS 4.x and up
__block MyDataProcessor *dp = self; // OK if you aren't using ARC
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}
위 세 가지 모두 결과를 유지하지 않고 참조를 제공하지만 모두 약간 다르게 동작합니다 __weak
. 객체가 해제되면 참조를 0으로 설정하려고 시도합니다. __unsafe_unretained
잘못된 포인터로 당신을 떠날 것입니다; __block
실제로 다른 수준의 간접 성을 추가하고 블록 내에서 참조 값을 변경할 수 있습니다 (이 경우에는 dp
다른 곳에서는 사용되지 않으므로 관련 이 없음).
최선 의 방법 은 변경할 수있는 코드와 변경할 수없는 코드에 따라 다릅니다. 그러나 이것이 진행 방법에 대한 아이디어를 줬기를 바랍니다.