ARC 가능 코드에서“이 블록에서 [오브젝트]를 강력하게 캡처하면 유지주기가 발생할 수 있습니다.


141

ARC 가능 코드에서 블록 기반 API를 사용할 때 잠재적 유지주기에 대한 경고를 수정하는 방법은 무엇입니까?

경고 :
Capturing 'request' strongly in this block is likely to lead to a retain cycle

이 코드 스 니펫으로 생성됩니다.

ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:request.rawResponseData error:nil];
    // ...
    }];

경고는 request블록 내부의 개체 사용과 관련이 있습니다 .


1
당신은 아마 사용해야 responseData하는 대신 rawResponseData, 체크 ASIHTTPRequest 설명서를 참조하십시오.
0xced

답변:


165

나 자신에게 답장 :

설명서에 대한 이해는 키워드를 사용 block하고 변수를 블록 내에서 사용한 후 nil로 설정하는 것이 좋지만 여전히 경고를 표시한다고 말합니다.

__block ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:request.responseData error:nil];
    request = nil;
// ....

    }];

업데이트 : ' _block' 대신 '_ weak' 키워드를 사용하고 임시 변수를 사용하도록했습니다.

ASIHTTPRequest *_request = [[ASIHTTPRequest alloc] initWithURL:...
__weak ASIHTTPRequest *request = _request;

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:request.responseData error:nil];
    // ...
    }];

iOS 4도 타겟팅하려면 __unsafe_unretained대신을 사용하십시오 __weak. 동일한 동작이지만 객체가 파괴 될 때 포인터가 자동으로 nil로 설정되는 대신 매달려 있습니다.


8
ARC 문서를 기반으로 ARC 및 블록을 사용할 때와 동일한 동작을 얻으려면 __unsafe_unretained __block을 함께 사용해야하는 것처럼 들립니다.
헌터

4
@SeanClarkHess : 처음 두 줄을 결합하면 "보존 객체를 약한 변수에 할당; 할당 후 객체가 해제됩니다"
Guillaume

1
@Guillaume 응답에 감사드립니다. 일부 변수를 간과하고 시도했지만 경고가 사라졌습니다. 왜 이것이 작동하는지 아십니까? 컴파일러가 경고를 억제하도록 속이는 것입니까, 아니면 경고가 실제로 더 이상 유효하지 않습니까?
Chris Wagner

2
후속 질문을 게시했습니다 : stackoverflow.com/questions/8859649/…
barfoon

3
__block 및 __weak 키워드가 필요한 이유를 누군가가 설명 할 수 있습니까? 유지주기가 생성되는 것 같지만 보이지 않습니다. 임시 변수를 작성하면 문제가 어떻게 해결됩니까?
user798719

50

이 요청은 요청에 대한 강력한 참조가있는 요청에 블록을 할당하기 때문에 발생합니다. 블록은 자동으로 요청을 유지하므로 주기로 인해 원래 요청이 할당 해제되지 않습니다. 말이 되나요?

__block으로 요청 객체에 태그를 지정하여 자체를 참조 할 수 있기 때문에 이상합니다. 약한 참조를 옆에 만들어서이 문제를 해결할 수 있습니다 .

ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...];
__weak ASIHTTPRequest *wrequest = request;

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:wrequest.rawResponseData error:nil];
    // ...
    }];

__ 약한 ASIHTTPRequest * wrequest = 요청; 나를 위해 일하지 않았다. 오류가 발생했습니다. __block ASIHTTPRequest * blockRequest = request;
Ram G.

13

블록에 자기를 유지하기 때문에 발생합니다. 블록은 자체에서 액세스되며 자체는 블록에서 참조됩니다. 유지주기가 생성됩니다.

약한 참조를 생성하여이 문제를 해결하십시오. self

__weak typeof(self) weakSelf = self;

operationManager = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operationManager.responseSerializer = [AFJSONResponseSerializer serializer];
[operationManager setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {

    [weakSelf requestFinishWithSucessResponseObject:responseObject withAFHTTPRequestOperation:operation andRequestType:eRequestType];

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    [weakSelf requestFinishWithFailureResponseObject:error withAFHTTPRequestOperation:operation andRequestType:eRequestType];
}];
[operationManager start];

이것은 정답과 같은 주목해야한다
벤자민

6

때때로 xcode 컴파일러는 유지주기를 식별하는 데 문제가 있기 때문에 completionBlock을 유지하지 않는 경우 다음과 같이 컴파일러 플래그를 넣을 수 있습니다.

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"

-(void)someMethod {
}

1
어떤 사람들은 그것이 나쁜 디자인이라고 주장 할 수도 있지만 비동기 작업으로 끝날 때까지 메모리에 매달리는 독립적 인 객체를 만드는 경우가 있습니다. 이는 self에 대한 강력한 참조를 포함하여 의도적 인 유지주기를 작성하는 completionBlock 특성에 의해 유지됩니다. completionBlock에는 self.completionBlock = nil이 포함되어 있는데 completeBlock을 해제하고 유지주기를 중단하여 작업이 완료되면 객체가 메모리에서 해제되도록합니다. 귀하의 답변은이 작업을 수행 할 때 발생하는 경고를 해결하는 데 도움이됩니다.
hyperspasm

1
솔직히 말해서 하나가 옳고 컴파일러가 잘못되었을 가능성은 매우 적습니다. 따라서 경고를 뛰어 넘는 것은 위험한 사업입니다
Max MacLeod

3

Guillaume에서 제공하는 솔루션을 시도하면 디버그 모드에서는 모든 것이 정상이지만 릴리스 모드에서는 충돌합니다.

내 목표는 iOS 4.3이므로 __weak을 사용하지 않고 __unsafe_unretained를 사용하십시오.

객체 "요청"에서 setCompletionBlock :이 호출되면 코드가 충돌합니다.

따라서이 솔루션은 디버그 및 릴리스 모드에서 모두 작동합니다.

// Avoiding retain cycle :
// - ASIHttpRequest object is a strong property (crashs if local variable)
// - use of an __unsafe_unretained pointer towards self inside block code

self.request = [ASIHttpRequest initWithURL:...
__unsafe_unretained DataModel * dataModel = self;

[self.request setCompletionBlock:^
{
    [dataModel processResponseWithData:dataModel.request.receivedData];        
}];

재미있는 해결책. 왜 디버그 모드가 아닌 릴리스 모드에서 충돌하는지 알아 냈습니까?
Valerio Santinelli


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.