6

Có trường hợp nào cố ý tạo chu trình giữ lại để ngăn chặn deallocation, sau đó làm sạch nó sau này, là giải pháp tốt nhất cho một vấn đề?Cố ý tạo chu kỳ lưu giữ (Mục tiêu C không có GC)

Nếu có, có ví dụ nào trong số này trong khung công tác Cocoa Touch hoặc NextStep không?

Tôi muốn đặt câu hỏi này cụ thể cho Mục tiêu C với ARC vì Mục tiêu C với GC hoặc các ngôn ngữ khác với GC có thể hoạt động khác nhau.

+0

Không phải là giải pháp tốt hơn để giữ một tham chiếu mạnh mẽ bằng cách sử dụng một ivar hoặc biến thích hợp khác? – rmaddy

+0

Kiến thức phổ biến sẽ nói có. Nhưng đó là lý do tại sao tôi hỏi câu hỏi.Có lẽ có một trường hợp tạo ra một chu kỳ giữ lại (chẳng hạn như một nơi mà một đối tượng giữ lại chính nó) là giải pháp tốt nhất. – MikeS

+1

Đây là câu hỏi khó trả lời vì nó quá mở. Các câu hỏi với "bao giờ" hoặc "không bao giờ" và "tốt nhất" không thể được trả lời tốt. Sẽ tốt hơn nếu bạn có một trường hợp sử dụng cụ thể để hỏi. – rmaddy

Trả lời

7

Chắc chắn. Nó thực sự không phải là tất cả những gì không phổ biến, mặc dù bạn có thể không nhận ra nó. Ví dụ, giả sử rằng bộ điều khiển của tôi đang thực hiện yêu cầu mạng và tôi thực sự cần đảm bảo tôi xử lý phản hồi, ngay cả khi người dùng đã điều hướng khỏi bộ điều khiển đó.

tôi có thể làm điều gì đó như thế này:

- (void)doNetworkThing { 
    __block MyController *blockSelf = self; 

    NSURLRequest *request = // some request 
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler: 
     ^(NSURLResponse *response, NSData *data, NSError *error) { 
      // Handle the response here 
      [blockSelf doThingWithResponse:response]; 
     }]; 
} 

này giới thiệu một tầm thường duy trì chu kỳ nơi self đã gây ra bản thân để được giữ lại bằng cách gán cho bản thân để con trỏ mạnh blockSelf. Cho đến khi blockSelf vượt quá phạm vi, bản thân sẽ không bị hủy bỏ.

Lưu ý rằng thông thường, bạn sẽ sử dụng một con trỏ yếu trong tình huống này. Nhưng nếu bạn thực sự cần bộ điều khiển để xung quanh để xử lý nó, bằng cách sử dụng một con trỏ mạnh mẽ hoạt động quá. Ngay sau khi xử lý khối được deallocated, tham chiếu của nó để blockSelf sẽ biến mất. Kể từ khi các tài liệu tham khảo ngăn xếp để blockSelf cũng đi tự sau đó sẽ được deallocated nếu không ai khác đang nắm giữ vào nó.

Vì vậy, về cơ bản, blockSelf gây ra một chu kỳ lưu giữ tạm thời, rất hữu ích trong việc đảm bảo rằng deallocation không thể xảy ra cho đến khi yêu cầu hoàn tất. Vì ARC tự động dọn sạch số lượng lưu giữ khi biến __block vượt quá phạm vi, nó không giống như một chu trình giữ lại. Nhưng tuy nhiên, đó là những gì nó được.

+4

Mặc dù ví dụ tự giữ nguyên là tốt, bạn không bao giờ nên thực hiện truy cập mạng bên trong bộ điều khiển xem cụ thể vì chúng có thể biến mất bất kỳ lúc nào . Bạn chỉ nên thực hiện truy cập mạng trong các đối tượng mô hình (hoặc các bộ điều khiển mô hình) có tuổi thọ dài. Điều này là dễ dàng hơn nhiều để kiểm tra và tránh nhiều trường hợp góc gây phiền nhiễu. –

+0

@BJHomer, ví dụ điển hình. Tôi đã không nghĩ về kịch bản này. Tôi sẽ tiếp tục và xóa câu trả lời của tôi vì nó chỉ đơn giản là sai. – aLevelOfIndirection

1

Chắc chắn, có một số. Đáng chú ý nhất là NSURLConnection tạo ra một vòng lặp giữ lại với đại biểu của nó để đảm bảo rằng đại biểu không thể biến mất trước khi kết nối hoàn tất. Ít lý tưởng hơn, NSTimer tạo ra một vòng lặp giữ lại với mục tiêu của nó. Rất tiếc, điều này gây ra sự cố cho các bộ hẹn giờ lặp lại (xem RNTimer đối với một trong nhiều nỗ lực khắc phục sự cố này).

Nói chung, nếu một đối tượng ủy nhiệm có tuổi thọ rất ngắn so với đại biểu của nó, việc giữ lại đại biểu của nó là tự nhiên để tạo ra vòng lặp có lợi.

Nó ít phổ biến hơn, nhưng vẫn được thực hiện, đối với một số loại đối tượng nhất định để "tự giữ lại". Ví dụ, nếu bạn có một đối tượng mà bạn muốn tạo và thực hiện một số thao tác, và sau đó tự hủy, thì tự giữ lại có thể giải quyết vấn đề đó. Thông thường tốt hơn là nên tránh điều đó và để người gọi giữ lại đối tượng, nhưng nó vẫn có các công dụng của nó (tôi đã sử dụng nó để ghi nhật ký và các hoạt động không quan trọng khác mà tôi muốn kích hoạt và quên).

Điều đó nói rằng, nó không phải là không phổ biến trong mã của tôi để cố ý tạo ra các vòng giữ được tồn tại trong thời gian ngắn với các khối để đảm bảo rằng đối tượng gọi không thể biến mất trước khi khối kết thúc.