Khắc phục cảnh báo của Capturing [một đối tượng] mạnh trong khối này có khả năng dẫn đến một chu kỳ giữ lại mã trong mã kích hoạt ARC


141

Trong mã kích hoạt ARC, làm cách nào để khắc phục cảnh báo về chu kỳ lưu giữ tiềm năng, khi sử dụng API dựa trên khối?

Cảnh báo:
Capturing 'request' strongly in this block is likely to lead to a retain cycle

được sản xuất bởi đoạn mã này:

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

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

Cảnh báo được liên kết với việc sử dụng đối tượng requestbên trong khối.


1
Có lẽ bạn nên sử dụng responseDatathay vì rawResponseData, kiểm tra tài liệu ASIHTTPRequest.
0xced

Câu trả lời:


165

Trả lời bản thân:

Sự hiểu biết của tôi về tài liệu nói rằng sử dụng từ khóa blockvà đặt biến thành không sau khi sử dụng nó trong khối sẽ ổn, nhưng nó vẫn hiển thị cảnh báo.

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

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

    }];

Cập nhật: làm cho nó hoạt động với từ khóa '_ yếu' thay vì ' _block' và sử dụng biến tạm thời:

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

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

Nếu bạn cũng muốn nhắm mục tiêu iOS 4, hãy sử dụng __unsafe_unretainedthay vì __weak. Cùng một hành vi, nhưng con trỏ vẫn lơ lửng thay vì tự động được đặt thành con số không khi đối tượng bị phá hủy.


8
Dựa trên các tài liệu ARC, âm thanh như bạn cần sử dụng __unsafe_unretained __block cùng nhau để có được hành vi giống như trước đây khi sử dụng ARC và các khối.
Thợ săn

4
@SeanClarkHess: Khi tôi kết hợp hai dòng đầu tiên, tôi nhận được cảnh báo này: "Gán đối tượng được giữ lại cho biến yếu; đối tượng sẽ được giải phóng sau khi gán"
Guillaume

1
@Guillaume cảm ơn bạn đã phản hồi, một số cách tôi bỏ qua biến tạm thời, đã thử điều đó và các cảnh báo đã biến mất. Bạn có biết tại sao điều này hoạt động? Có phải nó chỉ lừa trình biên dịch để ngăn chặn các cảnh báo hoặc cảnh báo thực sự không còn hiệu lực?
Chris Wagner

2
Tôi đã đăng câu hỏi tiếp theo: stackoverflow.com/questions/8859649/ từ
barfoon

3
Ai đó có thể giải thích lý do tại sao bạn cần các từ khóa __block và __weak không? Tôi đoán có một chu trình giữ lại được tạo ra, nhưng tôi không thấy nó. Và làm thế nào để tạo một biến tạm thời khắc phục vấn đề?
dùng798719

50

Sự cố xảy ra do bạn đang gán một khối cho yêu cầu có tham chiếu mạnh để yêu cầu trong đó. Khối sẽ tự động giữ lại yêu cầu, vì vậy yêu cầu ban đầu sẽ không giải quyết vì chu kỳ. Có lý?

Điều này thật kỳ lạ bởi vì bạn đang gắn thẻ đối tượng yêu cầu bằng __block để nó có thể tự tham chiếu. Bạn có thể khắc phục điều này bằng cách tạo một tài liệu tham khảo yếu cùng với nó.

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

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

__weak ASIHTTPRequest * wrequest = request; đã không làm việc cho tôi. Đưa ra lỗi tôi đã sử dụng __block ASIHTTPRequest * blockRequest = request;
Ram G.

13

Nó gây ra do giữ lại bản thân trong khối. Khối sẽ được truy cập từ bản thân và bản thân được tham chiếu trong khối. điều này sẽ tạo ra một chu kỳ giữ lại.

Hãy thử giải quyết điều này bằng cách tạo ra một lời giới thiệu yếu về 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];

Đây là câu trả lời đúng và cần được lưu ý như vậy
Benjamin

6

Đôi khi trình biên dịch xcode gặp sự cố đối với định danh chu trình giữ lại, vì vậy nếu bạn chắc chắn rằng bạn không giữ lại xongBlock, bạn có thể đặt cờ trình biên dịch như sau:

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

-(void)someMethod {
}

1
Một số người có thể lập luận rằng đó là thiết kế xấu nhưng đôi khi tôi tạo ra các đối tượng độc lập tồn tại trong bộ nhớ cho đến khi chúng kết thúc với một tác vụ không đồng bộ. Chúng được giữ lại bởi một thuộc tính xongBlock chứa tham chiếu mạnh đến bản thân, tạo ra một chu kỳ giữ có chủ ý. CompleteBlock chứa self.completionBlock = nil, sẽ giải phóng xongBlock và phá vỡ chu trình giữ lại, cho phép đối tượng được giải phóng khỏi bộ nhớ sau khi nhiệm vụ hoàn thành. Câu trả lời của bạn rất hữu ích để giúp làm dịu những cảnh báo xảy ra khi tôi làm điều này.
quá

1
Thành thật mà nói, cơ hội của một người đúng và trình biên dịch sai là rất nhỏ. Vì vậy, tôi muốn nói rằng vượt qua các cảnh báo là kinh doanh rủi ro
Max MacLeod

3

Khi tôi thử giải pháp do Guillaume cung cấp, mọi thứ đều ổn ở chế độ Gỡ lỗi nhưng nó bị sập ở chế độ Phát hành.

Lưu ý rằng không sử dụng __weak nhưng __unsafe_unretained vì mục tiêu của tôi là iOS 4.3.

Mã của tôi bị sập khi setCompletionBlock: được gọi trên đối tượng "request": yêu cầu đã được giải quyết ...

Vì vậy, giải pháp này hoạt động cả trong chế độ Gỡ lỗi và Phát hành:

// 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];        
}];

Giải pháp thú vị. Bạn đã tìm ra lý do tại sao nó gặp sự cố trong chế độ Phát hành mà không phải trong Gỡ lỗi?
Valerio Santinelli


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.