Làm thế nào để cơ chế đếm tham chiếu tự động mới hoạt động?


206

Ai đó có thể giải thích ngắn gọn cho tôi cách ARC hoạt động không? Tôi biết nó khác với Bộ sưu tập rác, nhưng tôi chỉ tự hỏi chính xác nó hoạt động như thế nào.

Ngoài ra, nếu ARC thực hiện những gì mà GC làm mà không cản trở hiệu năng, thì tại sao Java lại sử dụng GC? Tại sao nó không sử dụng ARC là tốt?


2
Điều này sẽ cho bạn biết tất cả về nó: http://clang.llvm.org/docs/AutomaticReferenceCounting.html Cách thức triển khai trong Xcode và iOS 5 trong NDA.
Morten nhanh

14
@mbehan Đó là lời khuyên tồi. Tôi không muốn đăng nhập hoặc thậm chí có tài khoản cho trung tâm phát triển iOS, nhưng tôi vẫn muốn biết về ARC.
Andres F.

1
ARC không làm mọi thứ mà GC làm, nó yêu cầu bạn phải làm việc với ngữ nghĩa tham chiếu mạnh và yếu một cách rõ ràng và rò rỉ bộ nhớ nếu bạn không làm đúng. Theo kinh nghiệm của tôi, điều này ban đầu rất khó khăn khi bạn sử dụng các khối trong Objective-C và thậm chí sau khi bạn biết được các thủ thuật mà bạn để lại với một số mã soạn sẵn (IMO) gây phiền nhiễu xung quanh nhiều cách sử dụng các khối. Sẽ thuận tiện hơn khi chỉ cần quên đi các tham chiếu mạnh / yếu. Hơn nữa, GC có thể thực hiện phần nào tốt hơn ARC wrt. CPU, nhưng đòi hỏi nhiều bộ nhớ hơn. Nó có thể nhanh hơn quản lý bộ nhớ rõ ràng khi bạn có nhiều bộ nhớ.
TaylanUB

@TaylanUB: "đòi hỏi nhiều bộ nhớ hơn". Rất nhiều người nói vậy nhưng tôi thấy khó tin.
Jon Harrop

2
@JonHarrop: Hiện tại tôi thậm chí không nhớ tại sao tôi lại nói vậy, thành thật mà nói. :-) Trong khi đó, tôi nhận ra rằng có rất nhiều chiến lược GC khác nhau mà những tuyên bố như vậy có lẽ đều vô giá trị. Hãy để tôi đọc Hans Boehm từ Huyền thoại phân bổ trí nhớ và nửa sự thật của mình : "Tại sao khu vực này lại dễ bị những trí tuệ dân gian mơ hồ?"
TaylanUB

Câu trả lời:


244

Mỗi nhà phát triển mới đến với Objective-C đều phải học các quy tắc cứng nhắc về thời điểm giữ lại, phát hành và tự động phát hành đối tượng. Các quy tắc này thậm chí chỉ định các quy ước đặt tên ngụ ý số lượng đối tượng được giữ lại từ các phương thức. Quản lý bộ nhớ trong Objective-C trở thành bản chất thứ hai một khi bạn áp dụng các quy tắc này và áp dụng chúng một cách nhất quán, nhưng ngay cả các nhà phát triển Cacao có kinh nghiệm nhất cũng thỉnh thoảng trượt lên.

Với Trình phân tích tĩnh Clang, các nhà phát triển LLVM nhận ra rằng các quy tắc này đủ tin cậy để họ có thể xây dựng một công cụ để chỉ ra các rò rỉ bộ nhớ và phát hành quá mức trong các đường dẫn mà mã của bạn thực hiện.

Đếm tham chiếu tự động (ARC) là bước logic tiếp theo. Nếu trình biên dịch có thể nhận ra nơi bạn nên giữ lại và phát hành các đối tượng, tại sao nó không chèn mã đó cho bạn? Nhiệm vụ cứng nhắc, lặp đi lặp lại là những gì trình biên dịch và anh em của họ rất giỏi. Con người quên đi mọi thứ và phạm sai lầm, nhưng máy tính thì nhất quán hơn nhiều.

Tuy nhiên, điều này không hoàn toàn giải phóng bạn khỏi lo lắng về việc quản lý bộ nhớ trên các nền tảng này. Tôi mô tả vấn đề chính cần chú ý (giữ lại chu kỳ) trong câu trả lời của tôi ở đây , điều này có thể đòi hỏi một chút suy nghĩ từ phía bạn để đánh dấu con trỏ yếu. Tuy nhiên, đó là chuyện nhỏ khi so sánh với những gì bạn đạt được trong ARC.

Khi so sánh với quản lý bộ nhớ thủ công và thu gom rác, ARC cung cấp cho bạn khả năng tốt nhất của cả hai thế giới bằng cách loại bỏ nhu cầu viết mã giữ lại / giải phóng, nhưng không có hồ sơ bộ nhớ tạm dừng và răng cưa nhìn thấy trong môi trường thu gom rác. Về lợi thế duy nhất của việc thu gom rác là điều này là khả năng xử lý các chu kỳ giữ lại và thực tế là việc chuyển nhượng tài sản nguyên tử là không tốn kém (như được thảo luận ở đây ). Tôi biết tôi đang thay thế tất cả mã Mac GC hiện tại của mình bằng các triển khai ARC.

Về việc liệu điều này có thể được mở rộng sang các ngôn ngữ khác hay không, có vẻ như nó xoay quanh hệ thống đếm tham chiếu trong Objective-C. Có thể khó áp dụng điều này cho Java hoặc các ngôn ngữ khác, nhưng tôi không biết đủ về các chi tiết trình biên dịch cấp thấp để đưa ra một tuyên bố dứt khoát ở đó. Cho rằng Apple là người thúc đẩy nỗ lực này trong LLVM, Objective-C sẽ đến trước trừ khi một bên khác cam kết các nguồn lực quan trọng của riêng họ cho việc này.

Việc công bố các nhà phát triển gây sốc này tại WWDC, vì vậy mọi người không biết rằng có thể làm được điều gì đó như thế này. Nó có thể xuất hiện trên các nền tảng khác theo thời gian, nhưng hiện tại nó chỉ dành riêng cho LLVM và Objective-C.


56
nhấn mạnh của tôi: điều này không hoàn toàn giải phóng bạn khỏi lo lắng về quản lý bộ nhớ
bshirley

6
ARC có thực sự là một sự đổi mới? Từ câu trả lời của bạn, tôi kết luận rằng ARC là một khái niệm mới, lần đầu tiên được sử dụng trong Objective-C (sửa tôi nếu tôi sai). Thành thật mà nói, tôi không phải là nhà phát triển Objective-C và không biết nhiều về ARC, nhưng liệu Boost Shared Pointers (xem boost.org) không hoàn toàn giống nhau? Và nếu họ không, sự khác biệt là gì?
theDmi

2
@DMM - Thay vì dựa vào các toán tử quá tải (như Boost thực hiện), đây là một quá trình ở cấp độ trình biên dịch, nó mở rộng nó trên toàn bộ ngôn ngữ. Trong số những thứ khác, điều này giúp dễ dàng chuyển đổi một ứng dụng được tính tham chiếu thủ công sang ARC. Boost cũng có thể xử lý các biến cục bộ khác với ARC, trong đó ARC biết thời điểm một biến cục bộ không còn được sử dụng và có thể giải phóng tại thời điểm đó. Tôi tin rằng với Boost bạn vẫn cần xác định theo cách nào đó bạn đã thực hiện với biến.
Brad Larson

6
Để trả lời câu hỏi "nó có mới không", Delphi đã tự động đếm tham chiếu cho các chuỗi, mảng và giao diện (để hỗ trợ COM) trong hơn một thập kỷ. Tôi đồng ý rằng đó thực sự là một sự thỏa hiệp tốt đẹp giữa môi trường gc'd và môi trường "làm tất cả bằng tay". Tôi rất vui vì nó ở ObjC và LLVM (vì vậy các ngôn ngữ khác cũng có thể tận dụng lợi thế của nó).
davidmw

2
@theDmi: "ARC có thực sự là một sự đổi mới không?". Đếm tham chiếu tự động được phát minh vào năm 1960 và đã được sử dụng trong nhiều ngôn ngữ như Python và Mathicala. Nó không được sử dụng trong JVM hoặc CLR vì nó rất chậm và rò rỉ theo chu kỳ.
Jon Harrop

25

ARC chỉ chơi giữ lại / phát hành cũ (MRC) với trình biên dịch để tìm ra khi nào cần gọi giữ lại / phát hành. Nó sẽ có xu hướng có hiệu năng cao hơn, sử dụng bộ nhớ tối đa thấp hơn và hiệu suất dễ dự đoán hơn so với hệ thống GC.

Mặt khác, một số loại cấu trúc dữ liệu là không thể với ARC (hoặc MRC), trong khi đó, GC có thể xử lý chúng.

Ví dụ, nếu bạn có một lớp có tên là nút và nút có NSArray của trẻ em và một tham chiếu duy nhất đến cha mẹ của nó là "chỉ hoạt động" với GC. Với ARC (và tính tham chiếu thủ công cũng vậy), bạn có một vấn đề. Bất kỳ nút cho trước sẽ được tham chiếu từ con của nó và cũng từ cha mẹ của nó.

Giống:

A -> [B1, B2, B3]
B1 -> A, B2 -> A, B3 -> A

Tất cả đều ổn trong khi bạn đang sử dụng A (nói thông qua một biến cục bộ).

Khi bạn hoàn thành nó (và B1 / B2 / B3), một hệ thống GC cuối cùng sẽ quyết định xem xét mọi thứ mà nó có thể tìm thấy bắt đầu từ các thanh ghi ngăn xếp và CPU. Nó sẽ không bao giờ tìm thấy A, B1, B2, B3 vì vậy nó sẽ hoàn thiện chúng và tái chế bộ nhớ thành các đối tượng khác.

Khi bạn sử dụng ARC hoặc MRC và kết thúc với A, nó có tổng số 3 (B1, B2 và B3 đều tham chiếu đến nó) và B1 / B2 / B3 đều có số tham chiếu là 1 (NSArray của A giữ một tham chiếu đến mỗi). Vì vậy, tất cả những vật thể đó vẫn tồn tại mặc dù không có gì có thể sử dụng chúng.

Giải pháp chung là quyết định một trong những tài liệu tham khảo đó cần phải yếu (không đóng góp vào số tham chiếu). Điều đó sẽ hoạt động đối với một số mẫu sử dụng, ví dụ: nếu bạn tham chiếu B1 / B2 / B3 chỉ qua A. Tuy nhiên trong các mẫu khác thì không thành công. Ví dụ: nếu đôi khi bạn sẽ giữ B1 và ​​mong đợi trèo lên thông qua con trỏ cha và tìm A. Với tham chiếu yếu nếu bạn chỉ giữ B1, A có thể (và thường sẽ) bay hơi, và lấy B2 và B3 với nó.

Đôi khi điều này không phải là một vấn đề, nhưng một số cách làm việc rất hữu ích và tự nhiên với các cấu trúc dữ liệu phức tạp rất khó sử dụng với ARC / MRC.

Vì vậy, ARC nhắm mục tiêu cùng loại vấn đề mục tiêu GC. Tuy nhiên ARC hoạt động trên một tập các mẫu sử dụng hạn chế hơn sau đó là GC, vì vậy nếu bạn lấy ngôn ngữ GC (như Java) và ghép một cái gì đó như ARC lên nó, một số chương trình sẽ không hoạt động nữa (hoặc ít nhất sẽ tạo ra hàng tấn bộ nhớ bị bỏ rơi và có thể gây ra sự cố tráo đổi nghiêm trọng hoặc hết bộ nhớ hoặc trao đổi dung lượng).

Bạn cũng có thể nói ARC đặt ưu tiên lớn hơn cho hiệu suất (hoặc có thể dự đoán được) trong khi GC đặt ưu tiên lớn hơn là một giải pháp chung. Kết quả là GC có nhu cầu CPU / bộ nhớ ít dự đoán hơn và hiệu suất thấp hơn (thông thường) so với ARC, nhưng có thể xử lý bất kỳ kiểu sử dụng nào. ARC sẽ hoạt động tốt hơn nhiều đối với nhiều mẫu sử dụng phổ biến, nhưng đối với một vài mẫu sử dụng (hợp lệ!), Nó sẽ bị đổ và chết.


"Mặt khác, một số loại cấu trúc dữ liệu không thể thực hiện được với ARC" Tôi nghĩ rằng bạn có nghĩa là dọn dẹp tự động là không thể nếu không có gợi ý; rõ ràng, các cấu trúc dữ liệu là.
Steven Fisher

Chắc chắn, nhưng CHỈ dọn dẹp tự động các đối tượng ObjC có sẵn trong ARC nên "không dọn dẹp tự động" == "không dọn dẹp". Tôi sẽ tua lại sau đó trả lời khi tôi có nhiều thời gian hơn.
Sọc

@Stripes: tương đương với dọn dẹp thủ công trong ARC là phá vỡ chu kỳ thủ công, vd foo = nil.
Douglas

"[ARC] sẽ có xu hướng có hiệu suất cao hơn ... ARC đặt ưu tiên lớn hơn cho hiệu suất". Tôi ngạc nhiên khi đọc thấy rằng khi nó cũng được biết rằng tính tham khảo là nhiều chậm hơn so với truy tìm thu gom rác thải. Flyingfrogblog.blogspot.co.uk/2011/01/ khăn
Jon Harrop

2
Trong lý thuyết, GC nhanh hơn (mỗi thao tác đếm tham chiếu phải được kết hợp bộ đệm bộ đệm đa bộ xử lý và có rất nhiều trong số chúng). Trong thực tế, hệ thống GC duy nhất có sẵn cho ObjC chậm hơn nhiều. Điều cực kỳ phổ biến là các hệ thống GC tạm dừng các luồng vào các thời điểm ngẫu nhiên cho lượng thời gian có thể nhận biết của người dùng (có một số hệ thống GC thời gian thực, nhưng chúng không phổ biến và tôi nghĩ rằng chúng có các ràng buộc "thú vị")
Sọc

4

ma thuật

Nhưng cụ thể hơn ARC hoạt động bằng cách thực hiện chính xác những gì bạn sẽ làm với mã của mình (với một số khác biệt nhỏ nhất định). ARC là một công nghệ thời gian biên dịch, không giống như GC là thời gian chạy và sẽ ảnh hưởng tiêu cực đến hiệu suất của bạn. ARC sẽ theo dõi các tham chiếu đến các đối tượng cho bạn và tổng hợp các phương thức giữ lại / phát hành / tự động theo quy tắc thông thường. Bởi vì ARC này cũng có thể phát hành mọi thứ ngay khi chúng không còn cần thiết, thay vì ném chúng vào một bể tự động hoàn toàn vì lợi ích thông thường.

Một số cải tiến khác bao gồm không tham chiếu yếu, sao chép tự động các khối vào heap, tăng tốc trên bảng (6x cho nhóm tự động phát hành!).

Thảo luận chi tiết hơn về cách tất cả những thứ này hoạt động được tìm thấy trong Tài liệu LLVM trên ARC.


2
-1 "ARC là công nghệ biên dịch thời gian, không giống như GC là thời gian chạy và sẽ ảnh hưởng tiêu cực đến hiệu suất của bạn". Số lượng tham chiếu bị xáo trộn trong thời gian chạy rất không hiệu quả. Đó là lý do tại sao truy tìm các GC như JVM và .NET nhanh hơn nhiều.
Jon Harrop

1
@Jon: Bạn có bằng chứng về điều này? Từ cách đọc của riêng tôi, có vẻ như các thuật toán RC mới thường hoạt động tốt hoặc tốt hơn M & S GC.
xryl669

1
@ xryl669: Có một lời giải thích đầy đủ trong Cẩm nang GC ( gframbook.org ). Lưu ý rằng theo dõi! = M & S.
Jon Harrop

3

Nó thay đổi rất nhiều từ bộ sưu tập rác. Bạn đã thấy những cảnh báo cho bạn biết rằng bạn có thể bị rò rỉ các vật thể trên các đường khác nhau chưa? Những câu lệnh này thậm chí còn cho bạn biết bạn đã phân bổ đối tượng nào. Điều này đã được thực hiện một bước xa hơn và bây giờ có thể chèn retain/ releasebáo cáo tại các vị trí thích hợp, tốt hơn so với hầu hết các lập trình viên, gần như 100% thời gian. Thỉnh thoảng có một số trường hợp kỳ lạ của các đối tượng được giữ lại mà bạn cần giúp đỡ.


0

Giải thích rất tốt bởi tài liệu của nhà phát triển Apple. Đọc "Cách ARC hoạt động"

Để đảm bảo rằng các cá thể không biến mất trong khi chúng vẫn cần thiết, ARC theo dõi có bao nhiêu thuộc tính, hằng và các biến hiện đang đề cập đến từng thể hiện của lớp. ARC sẽ không phân bổ một cá thể miễn là vẫn còn ít nhất một tham chiếu hoạt động đến cá thể đó.

Để đảm bảo rằng các cá thể không biến mất trong khi chúng vẫn cần thiết, ARC theo dõi có bao nhiêu thuộc tính, hằng và các biến hiện đang đề cập đến từng thể hiện của lớp. ARC sẽ không phân bổ một cá thể miễn là vẫn còn ít nhất một tham chiếu hoạt động đến cá thể đó.

Để biết khác biệt. giữa bộ sưu tập Rác và ARC: Đọc này


0

ARC là một tính năng biên dịch cung cấp quản lý bộ nhớ tự động của các đối tượng.

Thay vì bạn phải nhớ khi sử dụng retain, release, vàautorelease ARC đánh giá các yêu cầu trọn đời của các đối tượng của bạn và tự động chèn các cuộc gọi quản lý bộ nhớ phù hợp cho bạn vào thời gian biên dịch. Trình biên dịch cũng tạo ra các phương thức dealloc thích hợp cho bạn.

Trình biên dịch chèn các retain/releasecuộc gọi cần thiết vào thời gian biên dịch, nhưng các cuộc gọi đó được thực thi trong thời gian chạy, giống như bất kỳ mã nào khác.

Sơ đồ sau đây sẽ cho bạn hiểu rõ hơn về cách thức hoạt động của ARC.

nhập mô tả hình ảnh ở đây

Những người mới phát triển iOS và chưa có kinh nghiệm làm việc trên Objective C. Vui lòng tham khảo tài liệu của Apple về Hướng dẫn lập trình quản lý bộ nhớ nâng cao để hiểu rõ hơn về quản lý bộ nhớ.

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.