Tại sao các đại biểu Objective-C thường được gán thuộc tính thay vì giữ lại?


176

Tôi đang lướt qua blog tuyệt vời được duy trì bởi Scott Stevenson và tôi đang cố gắng hiểu một khái niệm cơ bản của Objective-C về việc giao cho các đại biểu thuộc tính 'gán' so với 'giữ lại'. Lưu ý, cả hai đều giống nhau trong một môi trường thu gom rác. Tôi chủ yếu quan tâm đến một môi trường không dựa trên GC (ví dụ: iPhone).

Trực tiếp từ blog của Scott:

"Từ khóa gán sẽ tạo ra một setter gán trực tiếp giá trị cho biến thể hiện, thay vì sao chép hoặc giữ lại nó. Điều này tốt nhất cho các kiểu nguyên thủy như NSInteger và CGFloat hoặc các đối tượng bạn không sở hữu trực tiếp, chẳng hạn như đại biểu."

Điều đó có nghĩa là bạn không trực tiếp sở hữu đối tượng đại biểu? Tôi thường giữ các đại biểu của mình, bởi vì nếu tôi không muốn họ đi xuống vực thẳm, thì giữ lại sẽ chăm sóc điều đó cho tôi. Tôi thường trừu tượng UITableViewControll khỏi dataSource tương ứng và ủy nhiệm của nó. Tôi cũng giữ lại đối tượng cụ thể đó. Tôi muốn đảm bảo rằng nó không bao giờ biến mất để UITableView của tôi luôn có đại biểu của nó.

Ai đó có thể giải thích thêm về việc tại sao / tại sao tôi sai, vì vậy tôi có thể hiểu mô hình chung này trong lập trình Objective-C 2.0 về việc sử dụng thuộc tính gán cho đại biểu thay vì giữ lại không?

Cảm ơn!


Được trang bị lại với "đại biểu" và không có "iphone".
Quinn Taylor

tại sao đại biểu được chỉ định thay vì sao chép (như NSString?)
OMGPOP

Câu trả lời:


175

Lý do mà bạn tránh giữ lại các đại biểu là bạn cần tránh một chu kỳ giữ lại:

A tạo B A tự đặt mình là đại biểu của B, A được phát hành bởi chủ sở hữu của nó

Nếu B giữ lại A, A sẽ không được giải phóng, vì B sở hữu A, do đó, dealloc của A sẽ không bao giờ được gọi, khiến cả A và B bị rò rỉ.

Bạn không nên lo lắng về việc A sẽ biến mất vì nó sở hữu B và do đó loại bỏ nó trong dealloc.


Tôi không đồng ý, Mike. Tôi chỉ tìm thấy một vấn đề trong đó một phương thức có một đại biểu loại bỏ phương thức đó. Nhưng khi tôi thực hiện một cảnh báo bộ nhớ trong phương thức, nó sẽ giải phóng đại biểu. Sau đó, khi tôi đi để loại bỏ phương thức của mình, đại biểu là con số không. Tai nạn.
Paul Shapiro

Ok tôi không đồng ý, nhưng bạn nói đúng đó là một lỗi thiết kế. Tôi phát hiện ra rằng tôi đã chuyển cuộc gọi đuổi việc của mình thông qua một lớp con của người đuổi việc thực sự. Lớp con đó đã được giải phóng và không thể thông qua đại biểu container của phương thức để bãi nhiệm. Tôi đã thay đổi nó để chuyển một con trỏ đến đại biểu cuối cùng và người đó đã không được phát hành trong cảnh báo bộ nhớ và tất cả đều tốt.
Paul Shapiro

2
Mã của bạn không bao giờ nên được viết theo cách mà một đại biểu không làm cho nó bị sập. Chỉ có đối tượng sở hữu nên có một tài liệu tham khảo sở hữu. Khi dealloc'd, nó phải đặt các đại biểu của các đối tượng sở hữu thành con số không trước khi phát hành chúng. Sau đó, bất kỳ tin nhắn được gửi đến đại biểu nil chỉ đơn giản là bỏ qua. Tuy nhiên, vượt qua một đối tượng không trong một tin nhắn có thể sụp đổ. Chỉ cần chắc chắn rằng bạn không đối phó với các đại biểu theo cách đó.
David Gish

Đợi đã - không phải là cái gì weaksao? Câu hỏi là tại sao sử dụng assignthay vì weak?
wcochran

3
@wcochran: Không, câu hỏi này là tại sao sử dụng assignthay vì retain. Câu hỏi cũ hơn ARC; weakstrong(sau này đồng nghĩa với retain) đã không tồn tại cho đến khi ARC được giới thiệu. Bạn nên đặt câu hỏi của bạn về weakso với assignriêng biệt.
Peter Hosey

44

Bởi vì đối tượng gửi tin nhắn đại biểu không sở hữu đại biểu.

Nhiều khi, đó là cách khác, khi bộ điều khiển tự đặt mình là đại biểu của chế độ xem hoặc cửa sổ: bộ điều khiển sở hữu chế độ xem / cửa sổ, vì vậy nếu chế độ xem / cửa sổ sở hữu đại biểu của nó, cả hai đối tượng sẽ sở hữu lẫn nhau. Tất nhiên, đây là một chu trình giữ lại, tương tự như rò rỉ với hậu quả tương tự (các đối tượng nên chết vẫn còn sống).

Những lần khác, các đối tượng là đồng nghiệp: không ai sở hữu người kia, có lẽ vì cả hai đều thuộc sở hữu của cùng một đối tượng thứ ba.

Dù bằng cách nào, đối tượng với đại biểu không nên giữ lại đại biểu của mình.

(Nhân tiện, có ít nhất một ngoại lệ. Tôi không nhớ nó là gì và tôi không nghĩ có lý do chính đáng cho việc đó.)


Phụ lục (đã thêm 2012-05-19): Trong ARC, bạn nên sử dụng weakthay vì assign. Tham chiếu yếu được đặt thành niltự động khi đối tượng chết, loại trừ khả năng đối tượng ủy nhiệm sẽ kết thúc việc gửi tin nhắn đến đại biểu đã chết.

Nếu bạn tránh xa ARC vì một số lý do, ít nhất hãy thay đổi assigncác thuộc tính trỏ đến các đối tượng unsafe_unretained, điều này cho thấy rõ đây là một tham chiếu không được lưu trữ nhưng không bằng không đối với một đối tượng.

assign vẫn phù hợp với các giá trị không phải đối tượng trong cả ARC và MRC.


13
NSURLConnectiongiữ lại đại biểu của nó.

Có, sử dụng weak, nhưng điều đó không trả lời câu hỏi ban đầu: Tại sao Apple sử dụng assignthay vì weak?
wcochran

@wcochran: Câu hỏi ban đầu là tại sao các thuộc tính đại biểu được đưa ra assignthay vì giữ lại ; weakđã không tồn tại khi nó được yêu cầu. Câu hỏi của bạn là một câu hỏi khác nhau, mà bạn nên hỏi riêng. Tôi rất vui được trả lời nó.
Peter Hosey

@wcochran và Peter, câu hỏi đó có được hỏi ở nơi nào khác không?
Ông Rogers

17

Lưu ý rằng khi bạn có một đại biểu được chỉ định, điều rất quan trọng là luôn luôn đặt giá trị ủy nhiệm đó thành nil bất cứ khi nào đối tượng sẽ được xử lý - vì vậy một đối tượng phải luôn cẩn thận để loại bỏ các tham chiếu ủy nhiệm trong dealloc nếu nó không làm như vậy ở nơi khác


Lưu ý rằng khi bạn có một đại biểu được chỉ định, điều rất quan trọng là luôn luôn đặt giá trị đại biểu đó thành không cho đến khi nào đối tượng sẽ bị xử lý vì sao?
Peter Hosey

2
Bởi vì bất kỳ tham chiếu nào được đặt, sẽ không hợp lệ sau khi một đối tượng được giải phóng (chỉ vào bộ nhớ không còn được phân bổ cho loại đối tượng dự kiến) - và do đó gây ra sự cố nếu bạn cố gắng sử dụng nó. Một dấu hiệu của điều này trong trình gỡ lỗi là khi trình gỡ lỗi tuyên bố một số biến có một loại dường như hoàn toàn sai so với biến thực sự được khai báo là.
Kendall Helmstetter Gelner

1
Điều này chỉ cần thiết nếu đối tượng mà bạn là đại biểu đang được giữ bởi một nguồn khác, chẳng hạn như bộ đếm thời gian hoặc gọi lại không đồng bộ khác. Nếu không, nó sẽ bị xử lý sau khi bạn phát hành nó và sẽ không cố gắng gọi các phương thức ủy nhiệm.
Andrew Pouliot

@Andrew: Điều đó đúng, nhưng nếu bạn luôn biến nó thành một thông lệ để loại bỏ các đại biểu thì bạn sẽ không quên khi nó quan trọng, hoặc nếu bạn vô tình giữ lại một vật bị giữ và dù sao nó vẫn ở xung quanh. Nếu bạn không xử lý đại biểu thì kết quả chỉ là rò rỉ, thay vì rò rỉ sau đó là sự cố.
Kendall Helmstetter Gelner

1

Một trong những lý do đằng sau đó là để tránh chu kỳ giữ lại. Chỉ để tránh kịch bản A và B cả hai đối tượng tham chiếu lẫn nhau và không ai trong số chúng được giải phóng khỏi bộ nhớ.

Việc gán tạm thời là tốt nhất cho các loại nguyên thủy như NSInteger và CGFloat hoặc các đối tượng bạn không trực tiếp sở hữu, chẳng hạn như đại biểu.


Điều đó được sao chép từ trích dẫn của OP và câu trả lời được chấp nhận tương ứng, phải không?
dakab
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.