2012-06-05 4 views
3

블록, 내가 원하는 것을 정확하게 설명하지 않습니다 전무 : Objective C Blocks as Async-callbacks & BAD ACCESS블록과 비동기 콜백의 dealloc 개체 - 필요 비슷한 질문이 여기에있다

을 내가 함께 서비스를 호출하는 뷰 컨트롤러를 가지고 비동기 콜백. 콜백은 뷰 컨트롤러의 변수를 참조하여 블록을 채우는 블록을 사용하여 수행됩니다.

그것은 그래서 다음과 같습니다

- (void) loadData { 
    __block MyViewController *me = self; 
    [self.service executeWithCompletion:^(NSArray *result, NSError *error) { 
     if (!error) { 
      me.data = result; 
     } 
    }]; 
} 

을하지만, 내가보기 컨트롤러를 할당 해제의 경우, '나'는 다음 심하게 콜백에 의해 액세스 할 수 있습니다.

'나를'NULL로 만드는 가장 간단한 방법은 무엇입니까? iVar로 넣으면 순환 참조가 다시 나타납니다 ... 생각하십니까?

는 내가 분명 뭔가 ....

감사

답변

4

당신이 (나중에 또는 Mac OS X 10.7 이상) 아이폰 OS 5.0 이상을 대상으로하는 누락 것 같아? 그렇다면 __block 대신 ARC 및 __weak 변수를 사용할 수 있습니다. 이것은 참조 된 객체가 할당 해제 될 때 자동으로 제로 아웃됩니다. 코드는 다음과 같습니다.

- (void)loadData { 
    __weak MyViewController *me = self; 
    [self.service executeWithCompletion:^(NSArray *result, NSError *error) { 
     if (!error) { 
      MyViewController *strongMe = me; // load __weak var into strong 
      if (strongMe) { 
       strongMe.data = result; 
      } 
     } 
    }]; 
} 

이전 OS에 대한 지원이 필요한 경우 다른 해결책을 찾아야합니다. 한 가지 해결책은 방금 진행하여 블록에 self을 보유하게하는 것입니다. 서비스가 완료 블록을 실행하도록 보장 된 후 해제하면 완료 블록이 실행될 때 자동으로 중단되는 임시 주기만 생성됩니다. 또는 취소 후 블록을 호출 할 수 없도록 서비스를 취소 할 수있는 방법이있는 경우 __block을 계속 사용하고 -dealloc에서 서비스를 취소해야합니다. 다른 대안도 있지만 더 복잡합니다.

+0

아직 전체 프로젝트를 ARC로 이전 할 수 없습니다. 너무 큽니다. 어쨌든 iOS 4를 타겟팅하고 있습니다. 예, 서비스에 취소를 추가하는 중입니다. 어떻게 든 __block 변수를 iVar 범위에 넣을 수 없으며 dealloc에서 nill/NULL을 사용할 수 있습니까? 이것은 내가 언급 한 광산과 비슷한 게시물에 언급되어 있습니다. – bandejapaisa

+0

@bandejapaisa : 아니요, '__block'은 그렇게 작동하지 않습니다. –

+0

@bandejapaisa : 블록 전에 어떻게 하시겠습니까? 그것을 취소하거나 동일한 충돌 문제가있는 방법이 필요합니다. – user102008

0

피하려고하는 실제 유지 사이클 문제가 있습니까? self-executeWithCompletion: 완료 될 때까지 단순히 보유되어서는 안되는 이유가 있습니까? 그것이 완료되지 않을 실제적인 기회가 있습니까?

실제로는 실패로 끝나고 블록을 호출 한 후 (아마도 속성을 nil으로 설정하여) 해제하면 유지주기가 결국 중단되고 모든 것이 잘.

+0

글쎄 내 디자인에 결함이있을 수 있습니다. 그러나 명확한 유지주기가 있습니다. 네트워크 콜은 꽤 빨리 돌아오고,보기 컨트롤러가 해제되지 않는다는 것을 즉시 알 수 있습니다 (dealloc은 호출되지 않습니다). 그런 다음 계측기 누출과 사이클을 통해 실행하면 문제가있는 곳을 확인할 수 있습니다. 이를 __block으로 만들면이 유지주기가 확실히 수정되고 악기에서도이를 확인합니다. – bandejapaisa

+0

내가 말했듯이 블록을'-executeWithCompletion : '뒤에있는 코드로 호출 한 후에 블록을 릴리스해야합니다. 해당 코드를 표시해보십시오. –

+0

내 코드 스 니펫을 보면 MyViewController가 'service'를 유지하고 'service'에 지정된 블록은 MyViewController를 유지합니다 (__block을 사용하지 않으면). 그래서 사이클이 있습니다. – bandejapaisa

1

나는 제안에서 위의 것들을 조합했다. 블록을 nilling하는 것을 포함합니다. 그래도 내 물건은 아직 공개되지 않습니다. 즉, MyViewController의 dealloc에 ​​중단 점을 넣었습니다. __block 변수가 없으면 (아마도 비동기 연결로 인해) 훨씬 늦은 시점에 호출 될 수도 있고 때로는 전혀 호출되지 않을 수도 있습니다.

코드가 상당히 복잡하므로 위의 제안대로 작동하지 않는 다른 것들이 있다고 생각합니다.

Mike Ash의 MAZeroingWeakRef도 사용되었습니다. @KevinBallard가 제안한 __weak을 사용하는 것과 같습니다.

다음은 구현 한 방법이며 작동하는 것 같습니다. Dealloc은 내가 원하는 View Controller의 폐기 즉시 호출됩니다. 그리고 나는 그것을 충돌시킬 수 없습니다 ... 그리고 내가 넣은 기록 주석으로, 나는 이미 내가 총알을 피하고 있음을 볼 수 있습니다.

- (void) loadData { 
    __block MAZeroingWeakRef *zeroWeakRef = [[MAZeroingWeakRef alloc] initWithTarget:self]; 
    [zeroWeakRef setCleanupBlock: ^(id target) { 
     [zeroWeakRef autorelease]; 
    }]; 
    [self.service executeWithCompletion:^(NSArray *result, NSError *error) { 
     MyViewController *me = [zeroWeakRef target]; 
     if (!me) { 
      DULog(@"dodged a bullet"); 
     } 
     if (!error) { 
      me.data = result; 
     } 
    }]; 
}