A giữ lại chu kỳ xảy ra khi hai đối tượng lưu trữ tham chiếu mạnh mẽ với nhau. Trường hợp đơn giản nhất là đối tượng a
lưu trữ tham chiếu mạnh đến đối tượng b
và b
thực hiện ngược lại [1]. Chu kỳ lưu giữ là một vấn đề trong Objective-C bởi vì chúng làm cho ARC tin rằng các đối tượng này luôn được sử dụng ngay cả khi các đối tượng này không được tham chiếu từ bất kỳ nơi nào khác.
Hãy xem lại một số ví dụ. Bạn có đối tượng z
phân bổ a
và b
, sử dụng chúng và sau đó xử lý chúng.Nếu a
và b
tạo chu kỳ giữ chân giữa chúng ở địa điểm đầu tiên, a
và b
sẽ không bị phân phối lại. Nếu bạn làm điều đó nhiều lần bạn sẽ bị rò rỉ nghiêm trọng bộ nhớ.
Một ví dụ thế giới thực sự của một chu kỳ giữ lại là nếu a
giao đất và mạnh mẽ tham chiếu đến một đối tượng b
, nhưng bạn cũng lưu trữ một tham chiếu mạnh b
-a
(nhiều vật thể nhỏ trong đồ thị đối tượng có thể cần phải truy cập vào cha mẹ).
Các giải pháp thông thường nhất trong các trường hợp này là đảm bảo rằng các đối tượng chứa chỉ có tham chiếu yếu đối với các đối tượng chứa của nó và cũng đảm bảo rằng các đối tượng anh chị em không chứa tham chiếu mạnh mẽ với nhau.
Một giải pháp khác (thường ít thanh lịch hơn, nhưng có thể thích hợp trong một số trường hợp) có thể có một số phương thức tùy chỉnh cleanup
trong a
mà tham chiếu đến b
. Do đó, b
sẽ bị phân phối khi cleanup
được gọi (nếu b
không được tham chiếu mạnh ở nơi khác). Điều này là cồng kềnh vì bạn không thể làm điều này từ a
's dealloc
(nó không bao giờ được gọi là nếu có một chu kỳ giữ lại) và bởi vì bạn phải nhớ gọi cleanup
vào thời điểm thích hợp.
- Lưu ý rằng duy trì chu kỳ cũng là bắc cầu (ví dụ, đối tượng
a
mạnh tham chiếu b
mà mạnh mẽ tham chiếu c
mà mạnh mẽ tham chiếu a
).
Với tất cả điều này cho biết: quản lý bộ nhớ của khối là khá khó khăn để hiểu được.
Ví dụ đầu tiên của bạn có thể tạo tạm thời chu kỳ lưu giữ (và chỉ khi đối tượng self
của bạn lưu trữ tham chiếu mạnh mẽ đến someObject
). Chu kỳ lưu giữ tạm thời này sẽ biến mất khi khối kết thúc thực thi và được deallocated.
Trong thực hiện, self
sẽ lưu trữ một tham chiếu đến someObject
, someObject
đến block
, và block
-self
một lần nữa. Nhưng một lần nữa, nó chỉ là tạm thời vì khối không được lưu trữ vĩnh viễn ở bất cứ nơi nào (trừ khi thực hiện [someObject successBlock:failure:]
thực hiện điều đó, nhưng điều đó không thường xuyên cho các khối hoàn thành).
Vì vậy, chu kỳ lưu giữ không phải là vấn đề trong ví dụ đầu tiên của bạn.
Nói chung, giữ lại các chu kỳ trong các khối chỉ là vấn đề nếu một số đối tượng lưu trữ khối thay vì thực thi nó trực tiếp. Sau đó, thật dễ dàng để thấy rằng self
tham chiếu mạnh mẽ đến block
và block
có tham chiếu mạnh mẽ đến self
. Lưu ý rằng truy cập bất kỳ ivar nào từ bên trong một khối sẽ tự động tạo tham chiếu mạnh mẽ đến self
trong khối đó.
Tương đương để đảm bảo rằng đối tượng chứa không tham chiếu mạnh mẽ vùng chứa của nó là sử dụng __weak SelfClass *weakSelf = self
để truy cập cả hai phương pháp và ivars (tốt hơn nếu bạn truy cập ivars thông qua accessors, như khi sử dụng thuộc tính).Tham chiếu của khối của bạn đến self
sẽ yếu (đó là không phải là bản sao, đó là một tài liệu tham khảo yếu yếu) và điều đó sẽ cho phép self
không được phân phối.
Có thể lập luận rằng thực hành tốt là luôn luôn sử dụng weakSelf
bên trong tất cả các khối, được lưu trữ hay không, chỉ trong trường hợp. Tôi tự hỏi tại sao Apple không đưa ra hành vi mặc định này. Làm điều này thường không làm bất cứ điều gì có hại cho mã khối, ngay cả khi thực sự không cần thiết.
__block
hiếm khi được sử dụng trên các biến trỏ đến đối tượng, vì Mục tiêu-C không thực thi bất biến của các đối tượng như vậy.
Nếu bạn có con trỏ đến đối tượng, bạn có thể gọi phương thức của nó và các phương pháp này có thể sửa đổi, có hoặc không có __block
. __block
là nhiều hơn (chỉ?) Hữu ích trên các biến của các loại cơ bản (int, float, vv). Xem here để biết điều gì sẽ xảy ra khi bạn sử dụng __block
với biến con trỏ đối tượng. Bạn cũng có thể đọc thêm về __block
trong số Blocks Programming Topics của Apple.
Chỉnh sửa: Lỗi cố định liên quan đến việc sử dụng __block
trên con trỏ đối tượng. Nhờ @KevinDiTraglia để chỉ nó.
giả sử rằng MyCLass thực hiện một bản sao là bản sao thực ... vì '-copyWithZone:' chỉ có thể giữ lại ... hoàn toàn hợp pháp và được thực hiện chỉ trong bất kỳ đối tượng bất biến nào. –
@GradyPlayer: Có lẽ tôi đã thể hiện bản thân mình, nhưng ý tôi là khối sẽ lưu một con trỏ mạnh (hoặc yếu) trong bối cảnh khối của nó với * nội dung hiện tại * của 'self' (hoặc' me'). Phương thức 'copy' * * không liên quan. –
Vâng đôi khi SO quay trở lại đầu trang khi ai đó làm điều gì đó với họ ... và đôi khi tôi phải có một vài tháng hoặc vài tháng sau đó ... nhưng các đối tượng có thể được sao chép khi chụp khối vì vậy tôi không nghĩ rằng đó là không chính xác ... –