2014-01-08 2 views
1

완료 블록을 내 메서드에 전달하면이 완료 블록은 네트워크 요청이 완료되면 백그라운드에서 호출됩니다. 호출 객체가 그 동안 해제되는 경우 불행하게도, 응용 프로그램 충돌 :좀비가 백그라운드 스레드에서 완료 블록을 호출 할 때

의 ViewController (이 탐색 스택에서 팝 있기 때문에 할당이 해제 될 수 있습니다) 코드 :

__unsafe_unretained ViewController *weakSelf = self; 

[[URLRequester instance] sendUrl:url successBlock:^(id JSON) { 
    [weakSelf webserviceCallReturned:JSON]; 
}]; 

URLRequester - 코드 (간단하게 물론) :

- (void)sendUrl:(NSString *)urlAfterHost successBlock:(void (^)(id))successBlock { 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
     sleep(2); 
     successBlock(nil); 
     return; 
    }); 
} 

이 2 초 내에 ViewController가 탐색 스택에서 팝되면 응용 프로그램이 충돌합니다. 내가 뭘 놓치고 있니?

+0

if (weakSelf) {...}'? – Putz1103

+0

불행히도 weakself가 설정됩니다. – swalkner

+1

__unsafe__unretained를 __weak으로 변경할 수 있습니다. __unsafe__unretained는 읽지 않은 반면 __weak은 릴리스 된 객체를 처리합니다. – Putz1103

답변

2

__unsafe_unretained을 사용하면 개체가 할당이 해제 된 후에도 참조가 계속 남아 있습니다. 따라서 뷰 컨트롤러가 팝되면, weakSelf은 할당 해제 된 오브젝트를 가리키고 있습니다.

대신 __weak으로 변경하면보기 컨트롤러가 할당 해제되면 weakSelfnil으로 설정하면 문제가 없습니다. nil의 메소드를 호출해도 아무런 효과가 없으므로 weakSelf이 설정되어 있는지 확인하지 않아도됩니다.

+0

귀하의 충고는 문제를 해결해야한다는 점에서 좋지만 약간 동의하지 않습니다. 제 응답을 참조하십시오. – danh

+0

그리고 뷰 컨트롤러가 이미 팝되어있는 경우 문제가 될 수있는 메서드를 실행하고 싶지 않으므로 약한 상태를 유지해야하므로 답변에 동의하지 않습니다. 뷰 계층 구조. – Gavin

+0

"문제"에 대한 예를 들어 줄 수 있습니까? vc가 네비게이션 스택 위에있을 때도 문제가되지 않을 것이라고 생각할 수 없습니다. 어쨌든, 저의 대답은 약한 속성에 대한 독자의 지식에 의존하지 않는 테스트에 유의하십시오. – danh

2

블록 안에있는 '자아'는 항상 약한 (또는 유지되지 않은) 사본이어야한다고 생각하는 사람들이 많습니다. 그럴 수는 없습니다 **.

이 상황에서 좀비를 떠나면 오해가 충돌을 일으키는 것입니다. 정상적인 코드를보고 싶을 때처럼 블록 (unsafe_unretained가 아니라 weak이 아닌)에서 직접 self를 참조하는 것이 옳은 일입니다. 이 효과는 블록이 'self'(이 경우보기 컨트롤러)가 가리키는 인스턴스를 유지하며 블록이 파괴 될 때까지 (아마도 URL 요청자에 의해) 파괴되지 않습니다.

팝업 요청이 웹 요청의 결과를 처리하는 데 View Controller를 손상시킬 수 있습니까? 거의 확실하지는 않지만, 그렇게 될 것이라고 생각되면 블록에서 그 상태를 확인하십시오.

if (![self.navigationController.viewControllers containsObject:self]) 
    // I must have been popped, ignore the web request result 

    // Re the discussion in comments, I think a good coder should have misgivings about 
    // this condition. If you think you need it, ask yourself "why did I design 
    // my object so that it does something wrong based on whether some other object 
    // (a navigation vc in this case) contains it?" 

    // In that sense, the deliberate use of weakSelf is even worse, IMO, because 
    // it lets the coder ignore _and_obscure_ an important question. 
else { 
    // do whatever i do when the web request completes 
} 

** 블록에서 weak 포인터 또는 유지되지 않은 포인터가 필요하다는 것은 블록이 참조하는 개체를 유지한다는 사실 때문입니다. 이러한 객체 중 하나가 직접 또는 간접적으로 블록을 보유하면주기 (A는 B를 보유하고 A는 보유 함)와 누출을 얻습니다. 이것은 'self'뿐만 아니라 블록이 참조하는 모든 객체에서 발생할 수 있습니다.

하지만 (많은 경우) self가 참조하는보기 컨트롤러는 블록을 보유하지 않습니다.

+0

큰 설명, 감사합니다! 그러나'webserviceCallReturned :'메쏘드가 호출되지 않았기 때문에'weak'가 더 좋지는 않을 것입니다. (VC가 pop되면 필요하지 않습니다.) – swalkner

+1

흥미로운 질문입니다. 다른 대답이 지적했듯이 약한 선언은 바람직한 결과를 가져올 것입니다. 그러나 이것은 부작용으로 생각해야한다고 생각합니다. 나는 약점의 귀여운 (내 취향에 너무 귀엽다) 부작용보다 코드 단순성 (여분의 한정어 없음)과 코드 선명도 (필요할 경우 블록의 명시 적 조건)를 선호한다고 생각한다. – danh

+0

나는 여기서 약한 것이 더 낫다는 것에 동의한다. 나는 그것이 항상 필요한 것은 아니지만, 그것이 터지거나 그렇지 않으면 할당을 해제해야한다면, 여기서는 아무것도해서는 안된다. 이미 터 졌을 때 뭔가를하려고하면 어떤 경우 실제로 문제가 발생할 수 있습니다. – Gavin

0

블록 (특히 지연 블록)을 사용하는 좋은 방법은 호출 메소드에서 블록의 로컬 복사본을 만드는 것입니다. successBlock :

successBlockCopy = [successBlock copy]; 

을 한 후 완료 될 때까지 잠시 동안의 ViewController를 유지해야

successBlockCopy(nil); 

전화 (무효) sendUrl - 귀하의 경우에는 그것을에서이 작업을 수행해야합니다.

갑자기 출시 된 개체에 대한 문제를 피하려면 __weak 대신 __unsafe_unretained를 사용하는 것이 좋습니다.

+1

이것은 dispatch_async에 의해 실행되는 블록이 successBlock을 캡처하여 사본을 생성하기 때문에 실제로는 필요하지 않습니다. 따라서 다시 복사 할 필요가 없습니다. 단지 중복됩니다. – Gavin