กลไกการนับการอ้างอิงอัตโนมัติใหม่ทำงานอย่างไร


206

บางคนสามารถอธิบายสั้น ๆ ว่า ARC ทำงานอย่างไร ฉันรู้ว่ามันแตกต่างจาก Garbage Collection แต่ฉันแค่สงสัยว่ามันทำงานอย่างไร

นอกจากนี้หาก ARC ทำในสิ่งที่ GC ทำโดยไม่มีการขัดขวางประสิทธิภาพแล้วเหตุใด Java จึงใช้ GC ทำไมมันไม่ใช้ ARC เช่นกัน?


2
สิ่งนี้จะบอกคุณเกี่ยวกับเรื่องนี้: http://clang.llvm.org/docs/AutomaticReferenceCounting.htmlวิธีการใช้งานใน Xcode และ iOS 5 อยู่ภายใต้ NDA
Morten Fast

14
@mbehan นั่นเป็นคำแนะนำที่ไม่ดี ฉันไม่ต้องการเข้าสู่ระบบหรือมีบัญชีสำหรับ iOS dev center แต่ฉันสนใจที่จะรู้เกี่ยวกับ ARC
Andres F.

1
ARC ไม่ได้ทำทุกอย่างที่ GC ทำคุณต้องทำงานกับซีแมนทิกส์อ้างอิงที่แรงและอ่อนแออย่างชัดเจนและรั่วไหลหน่วยความจำหากคุณไม่ได้รับสิ่งที่ถูกต้อง จากประสบการณ์ของฉันนี่เป็นเรื่องยากครั้งแรกเมื่อคุณใช้บล็อกใน Objective-C และแม้กระทั่งหลังจากที่คุณเรียนรู้เทคนิคที่คุณทิ้งไว้ด้วยโค้ดสำเร็จรูปที่น่ารำคาญ (IMO) รอบ ๆ บล็อกการใช้งานมากมาย สะดวกกว่าที่จะลืมการอ้างอิงที่รัดกุมหรืออ่อนแอ นอกจากนี้ GC สามารถทำงานได้ค่อนข้างดีกว่า ARC wrt CPU แต่ต้องการหน่วยความจำเพิ่ม อาจเร็วกว่าการจัดการหน่วยความจำที่ชัดเจนเมื่อคุณมีหน่วยความจำจำนวนมาก
TaylanUB

@TaylanUB: "ต้องการหน่วยความจำเพิ่มเติม" หลายคนพูดแบบนั้น แต่ฉันคิดว่ามันยากที่จะเชื่อ
Jon Harrop

2
@ JonHarrop: ปัจจุบันฉันจำไม่ได้ด้วยซ้ำว่าทำไมถึงพูดอย่างนั้น :-) ในขณะเดียวกันฉันตระหนักว่ามีกลยุทธ์ GC ที่แตกต่างกันมากมายซึ่งข้อความแบบครอบคลุมดังกล่าวอาจไร้ค่าทั้งหมด ให้ฉันท่อง Hans Boehm จากตำนานการจัดสรรความทรงจำและความจริงครึ่งหนึ่งของเขา: "ทำไมบริเวณนี้จึงมีแนวโน้มที่จะเป็นภูมิปัญญาพื้นบ้านที่น่าสงสัย"
TaylanUB

คำตอบ:


244

นักพัฒนาใหม่ทุกคนที่มาถึง Objective-C จะต้องเรียนรู้กฎที่เข้มงวดของเวลาที่จะเก็บรักษาปล่อยและกำจัดวัตถุอัตโนมัติ กฎเหล่านี้ยังระบุอนุสัญญาการตั้งชื่อที่บ่งบอกถึงจำนวนการเก็บรักษาวัตถุที่ส่งคืนจากวิธีการ การจัดการหน่วยความจำใน Objective-C กลายเป็นลักษณะที่สองเมื่อคุณยึดถือกฎเหล่านี้และนำไปใช้อย่างสม่ำเสมอ แต่แม้กระทั่งนักพัฒนาโกโก้ที่มีประสบการณ์มากที่สุดก็เลื่อนเวลาออกไป

ด้วย Clang Static Analyzer ผู้พัฒนา LLVM ตระหนักว่ากฎเหล่านี้มีความน่าเชื่อถือเพียงพอที่พวกเขาสามารถสร้างเครื่องมือเพื่อชี้ให้เห็นถึงการรั่วไหลของหน่วยความจำและ overreleases ภายในเส้นทางที่รหัสของคุณใช้

การนับการอ้างอิงอัตโนมัติ (ARC) เป็นขั้นตอนตรรกะถัดไป หากคอมไพเลอร์สามารถจดจำตำแหน่งที่คุณควรเก็บและปล่อยวัตถุทำไมไม่ให้มันแทรกรหัสสำหรับคุณ? งานที่ทำซ้ำ ๆ อย่างเข้มงวดเป็นสิ่งที่คอมไพเลอร์และพี่น้องของพวกเขาเก่งกาจ มนุษย์ลืมสิ่งต่าง ๆ และทำผิดพลาด แต่คอมพิวเตอร์มีความสอดคล้องกันมากขึ้น

อย่างไรก็ตามคุณไม่ต้องกังวลเกี่ยวกับการจัดการหน่วยความจำบนแพลตฟอร์มเหล่านี้ ฉันอธิบายปัญหาหลักที่ต้องระวัง (รักษารอบ) ในคำตอบของฉันที่นี่ซึ่งอาจต้องใช้ความคิดเล็กน้อยในส่วนของคุณเพื่อทำเครื่องหมายตัวชี้อ่อนแอ อย่างไรก็ตามนั่นเป็นเรื่องเล็กน้อยเมื่อเทียบกับสิ่งที่คุณได้รับจาก ARC

เมื่อเปรียบเทียบกับการจัดการหน่วยความจำแบบแมนนวลและการรวบรวมขยะ ARC มอบสิ่งที่ดีที่สุดให้กับคุณทั้งสองโลกด้วยการตัดความจำเป็นในการเขียนรหัสการเก็บรักษา / การเปิดตัว แต่ยังไม่มีโปรไฟล์หน่วยความจำ เกี่ยวกับข้อได้เปรียบเพียงอย่างเดียวของการรวบรวมขยะคือความสามารถในการจัดการกับรอบการเก็บรักษาและความจริงที่ว่าการมอบหมายคุณสมบัติของอะตอมมีราคาไม่แพง (ดังที่อธิบายไว้ที่นี่ ) ฉันรู้ว่าฉันกำลังแทนที่โค้ด Mac GC ที่มีอยู่ทั้งหมดของฉันด้วยการใช้งาน ARC

สำหรับเรื่องนี้สามารถขยายออกไปเป็นภาษาอื่น ๆ ได้หรือไม่ดูเหมือนว่าจะเน้นไปที่ระบบการนับการอ้างอิงใน Objective-C อาจเป็นเรื่องยากที่จะใช้สิ่งนี้กับ Java หรือภาษาอื่น ๆ แต่ฉันไม่รู้เกี่ยวกับรายละเอียดของคอมไพเลอร์ระดับต่ำมากพอที่จะสร้างคำสั่งที่ชัดเจนได้ เนื่องจากแอปเปิ้ลเป็นผู้ผลักดันความพยายามใน LLVM นี้ Objective-C จะมาเป็นอันดับแรกยกเว้นในกรณีที่อีกฝ่ายหนึ่งมุ่งมั่นที่จะใช้ทรัพยากรที่สำคัญของตนเอง

การเปิดตัวนักพัฒนาที่น่าตกใจนี้ที่ WWDC ดังนั้นผู้คนจึงไม่ทราบว่าบางสิ่งเช่นนี้สามารถทำได้ มันอาจปรากฏบนแพลตฟอร์มอื่น ๆ เมื่อเวลาผ่านไป แต่ตอนนี้มันเป็นเอกสิทธิ์ของ LLVM และ Objective-C


56
เหมืองของฉันที่เน้น: สิ่งนี้ไม่ได้ช่วยให้คุณปลอดจากความกังวลเกี่ยวกับการจัดการหน่วยความจำ
bshirley

6
ARC เป็นนวัตกรรมหรือไม่ จากคำตอบของคุณฉันสรุปได้ว่า ARC เป็นแนวคิดใหม่ซึ่งใช้ใน Objective-C เป็นครั้งแรก (แก้ไขฉันหากฉันผิด) ความจริงแล้วฉันไม่ใช่นักพัฒนา Objective-C และไม่ค่อยรู้เรื่อง ARC มากนัก แต่เป็น Boost Pointers ที่ใช้ร่วมกัน (ดู boost.org) ไม่ใช่สิ่งเดียวกัน และถ้าพวกเขาไม่ได้ความแตกต่างคืออะไร?
theDmi

2
@DMM - แทนที่จะพึ่งโอเปอเรเตอร์ที่โอเวอร์โหลด (เช่นเดียวกับ Boost) นี่เป็นกระบวนการระดับคอมไพเลอร์ซึ่งขยายมันข้ามภาษาทั้งหมด เหนือสิ่งอื่นใดสิ่งนี้ทำให้ง่ายต่อการแปลงแอปพลิเคชันที่นับด้วยตนเองเป็น ARC Boost อาจจัดการกับตัวแปรท้องถิ่นที่แตกต่างจาก ARC โดยที่ ARC รู้ทันทีว่าไม่มีการใช้ตัวแปรท้องถิ่นอีกต่อไปและสามารถปล่อย ณ จุดนั้นได้ ฉันเชื่อว่าด้วย Boost คุณยังต้องระบุวิธีการที่คุณทำกับตัวแปร
Brad Larson

6
เพื่อตอบคำถาม "มันใหม่" Delphi มีการนับการอ้างอิงอัตโนมัติสำหรับสตริง, อาร์เรย์และอินเทอร์เฟซ (สำหรับการสนับสนุน COM) เป็นเวลากว่าทศวรรษ ฉันยอมรับว่ามันเป็นการประนีประนอมที่ดีระหว่างสภาพแวดล้อม gc'd และสภาพแวดล้อม "ทำทุกอย่างด้วยตนเอง" ฉันดีใจที่อยู่ใน ObjC และ LLVM (เพื่อให้ภาษาอื่นสามารถใช้ประโยชน์จากมันได้เช่นกัน)
davidmw

2
@theDmi: "ARC เป็นนวัตกรรมหรือไม่?" การนับการอ้างอิงอัตโนมัติถูกประดิษฐ์ขึ้นในปี 1960 และมีการใช้ในหลายภาษาเช่น Python และ Mathematica มันไม่ได้ใช้ใน JVM หรือ CLR เพราะมันช้ามากและรอบการรั่วไหล
Jon Harrop

25

ARC เป็นเพียงเล่นเก็บ / ปล่อยเก่า (MRC) กับคอมไพเลอร์หาเมื่อโทรเรียกเก็บ / ปล่อย มันมักจะมีประสิทธิภาพสูงขึ้นใช้หน่วยความจำสูงสุดต่ำกว่าและมีประสิทธิภาพที่คาดการณ์ได้มากกว่าระบบ GC

ในทางกลับกันโครงสร้างข้อมูลบางประเภทไม่สามารถทำได้ด้วย ARC (หรือ MRC) ในขณะที่ GC สามารถจัดการได้

ตัวอย่างเช่นหากคุณมีคลาสชื่อโหนดและโหนดมี NSArray ของ children และมีการอ้างอิงเดียวกับพาเรนต์ที่ "เพิ่งใช้งานได้" กับ GC ด้วย ARC (และการนับการอ้างอิงด้วยตนเองเช่นกัน) คุณมีปัญหา โหนดที่ระบุใด ๆ จะถูกอ้างอิงจากชายน์และจากพาเรนต์

ชอบ:

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

ทั้งหมดเป็นเรื่องปกติในขณะที่คุณใช้ A (พูดผ่านตัวแปรท้องถิ่น)

เมื่อเสร็จแล้ว (และ B1 / B2 / B3) ในที่สุดระบบ GC จะตัดสินใจดูทุกสิ่งที่สามารถหาได้เริ่มต้นจากสแต็กและการลงทะเบียน CPU มันจะไม่พบ A, B1, B2, B3 ดังนั้นมันจะทำการสรุปและรีไซเคิลหน่วยความจำไปยังวัตถุอื่น

เมื่อคุณใช้ ARC หรือ MRC และจบด้วย A จะมีจำนวน refcount 3 (B1, B2 และ B3 อ้างอิงทั้งหมด) และ B1 / B2 / B3 ทั้งหมดจะมีจำนวนการอ้างอิง 1 (NSArray ของ A มีการอ้างอิงหนึ่งรายการถึง แต่ละ). ดังนั้นวัตถุเหล่านั้นทั้งหมดยังคงอยู่แม้ว่าจะไม่มีอะไรสามารถใช้งานได้

วิธีแก้ปัญหาทั่วไปคือการตัดสินใจว่าหนึ่งในการอ้างอิงเหล่านั้นจำเป็นต้องอ่อนแอ (ไม่นำไปสู่การนับการอ้างอิง) สิ่งนี้จะใช้ได้กับรูปแบบการใช้งานบางอย่างเช่นหากคุณอ้างอิง B1 / B2 / B3 ผ่าน A เท่านั้น แต่ในรูปแบบอื่น ๆ จะล้มเหลว ตัวอย่างเช่นหากบางครั้งคุณจะกดค้างไว้ที่ B1 และคาดว่าจะปีนกลับขึ้นผ่านตัวชี้พาเรนต์และค้นหา A. ด้วยการอ้างอิงที่อ่อนแอหากคุณกดค้างไว้ที่ B1, A สามารถ (และโดยปกติจะ) ระเหยและรับ B2 และ B3 กับมัน

บางครั้งนี่ไม่ใช่ปัญหา แต่วิธีที่มีประโยชน์และเป็นธรรมชาติในการทำงานกับโครงสร้างที่ซับซ้อนของข้อมูลนั้นยากมากที่จะใช้กับ ARC / MRC

ดังนั้น ARC จึงตั้งเป้าหมายปัญหาประเภท GC ที่เหมือนกัน อย่างไรก็ตาม ARC ทำงานบนชุดรูปแบบการใช้งานที่ จำกัด มากขึ้นดังนั้น GC ดังนั้นถ้าคุณใช้ภาษา GC (เช่น Java) และกราฟต์บางอย่างเช่น ARC ลงบนโปรแกรมบางโปรแกรมจะไม่ทำงานอีกต่อไป (หรืออย่างน้อยก็จะสร้างหน่วยความจำที่ถูกทิ้งร้าง และอาจทำให้เกิดปัญหาการแลกเปลี่ยนที่รุนแรงหรือหน่วยความจำหมดหรือพื้นที่สว็อป)

คุณสามารถพูดได้ว่า ARC ให้ความสำคัญกับประสิทธิภาพ (หรืออาจคาดการณ์ได้มากขึ้น) ในขณะที่ GC ให้ความสำคัญกับการเป็นโซลูชันทั่วไปมากกว่า ด้วยเหตุนี้ GC จึงมีความต้องการซีพียู / หน่วยความจำที่คาดการณ์ได้น้อยกว่าและมีประสิทธิภาพที่ต่ำกว่า (ปกติ) กว่า ARC แต่สามารถรองรับรูปแบบการใช้งานใด ๆ ARC จะทำงานได้ดีขึ้นมากสำหรับรูปแบบการใช้งานทั่วไปจำนวนมาก แต่สำหรับรูปแบบการใช้งาน (ถูกต้อง!) เพียงเล็กน้อยมันจะล้มลงและตายไป


"ในอีกทางหนึ่งโครงสร้างข้อมูลบางประเภทไม่สามารถทำได้ด้วย ARC" ฉันคิดว่าคุณหมายถึงไม่สามารถล้างข้อมูลอัตโนมัติโดยไม่มีคำแนะนำ เห็นได้ชัดว่าโครงสร้างข้อมูล
Steven Fisher

แน่นอน แต่เฉพาะการล้างวัตถุ ObjC โดยอัตโนมัติมีให้ใน ARC ดังนั้น "no cleanup อัตโนมัติ" == "no cleanup" ฉันจะ reword แล้วตอบเมื่อฉันมีเวลามากขึ้น
Stripes

@Stripes: เทียบเท่ากับการทำความสะอาดด้วยตนเองใน ARC foo = nilคือการทำลายตนเองของวงจรเช่น
ดักลาส

"[ARC] มีแนวโน้มที่จะมีประสิทธิภาพสูงกว่า ... ARC ให้ความสำคัญกับประสิทธิภาพเป็นอย่างมาก" ฉันประหลาดใจที่จะอ่านว่าเมื่อมันเป็นที่รู้จักกันดีว่านับการอ้างอิงเป็นมากช้ากว่าการติดตามการเก็บขยะ flyingfrogblog.blogspot.co.uk/2011/01/…
Jon Harrop

2
ในทางทฤษฎี GC นั้นเร็วกว่า (การจัดการการนับจำนวนการอ้างอิงแต่ละรายการจะต้องมีตัวประมวลผลร่วมกันหลายแคชและมีจำนวนมาก) ในทางปฏิบัติระบบ GC ที่ใช้ได้เฉพาะสำหรับ ObjC นั้นช้ากว่ามาก นอกจากนี้ยังเป็นเรื่องธรรมดามากสำหรับระบบ GC ที่จะหยุดเธรดในเวลาสุ่มสำหรับผู้ใช้ที่รับรู้จำนวนเวลา (มีบางระบบ GC เรียลไทม์ แต่พวกเขาไม่ได้ร่วมกันและฉันคิดว่าพวกเขามีข้อ จำกัด "น่าสนใจ")
Stripes

4

มายากล

แต่โดยเฉพาะอย่างยิ่ง ARC ทำงานโดยทำสิ่งที่คุณจะทำกับรหัสของคุณ (มีความแตกต่างเล็กน้อย) ARC เป็นเทคโนโลยีเวลารวบรวมซึ่งแตกต่างจาก GC ซึ่งเป็นรันไทม์และจะส่งผลกระทบต่อประสิทธิภาพการทำงานของคุณ ARC จะติดตามการอ้างอิงไปยังวัตถุสำหรับคุณและสังเคราะห์วิธีการเก็บรักษา / ปล่อย / การเลิกอัตโนมัติตามกฎปกติ เพราะอาร์คนี้สามารถปล่อยสิ่งต่าง ๆ ได้ทันทีที่พวกเขาไม่ต้องการอีกต่อไปแทนที่จะทิ้งมันลงในสระว่ายน้ำอัตโนมัติที่หมดจดเพื่อประโยชน์ในการประชุม

การปรับปรุงอื่น ๆ รวมถึงการอ้างอิงศูนย์ที่อ่อนแอการคัดลอกบล็อกอัตโนมัติไปยังฮีปการเร่งความเร็วทั่วกระดาน (6x สำหรับพูลการกู้คืนอัตโนมัติ!)

การสนทนาโดยละเอียดเพิ่มเติมเกี่ยวกับวิธีการทำงานทั้งหมดนี้พบได้ในLLVM เอกสารบน ARC


2
-1 "ARC เป็นเทคโนโลยีการรวบรวมเวลาซึ่งแตกต่างจาก GC ซึ่งเป็นรันไทม์และจะส่งผลกระทบต่อประสิทธิภาพการทำงานของคุณ" จำนวนการอ้างอิงถูกชน ณ รันไทม์ซึ่งไม่มีประสิทธิภาพมาก นั่นเป็นสาเหตุที่การติดตาม GCs เช่น JVM และ. NET นั้นเร็วขึ้นมาก
Jon Harrop

1
@ จอน: คุณมีหลักฐานการนี้หรือไม่? จากการอ่านของฉันเองดูเหมือนว่าอัลกอริทึม RC ใหม่มักจะทำงานได้ดีหรือดีกว่า M&S GC
xryl669

1
@ xryl669: มีคำอธิบายแบบเต็มใน GC Handbook ( gchandbook.org ) โปรดทราบว่าการติดตาม! = M&S
Jon Harrop

3

มันแตกต่างอย่างมากจากการเก็บขยะ คุณเคยเห็นคำเตือนที่บอกคุณหรือไม่ว่าคุณกำลังรั่วสิ่งของต่าง ๆ ? ข้อความเหล่านั้นบอกคุณในบรรทัดที่คุณจัดสรรวัตถุ สิ่งนี้ได้ถูกดำเนินการไปอีกขั้นหนึ่งและตอนนี้สามารถแทรกretain/ releaseแถลงการณ์ในสถานที่ที่เหมาะสมดีกว่าโปรแกรมเมอร์ส่วนใหญ่เกือบ 100% ของเวลา ในบางครั้งมีบางสิ่งที่แปลกประหลาดของวัตถุที่ถูกเก็บไว้ซึ่งคุณต้องการช่วย


0

อธิบายได้ดีมากโดยเอกสารประกอบสำหรับนักพัฒนาของ Apple อ่าน"วิธีการทำงานของ ARC"

เพื่อให้แน่ใจว่าอินสแตนซ์จะไม่หายไปในขณะที่ยังต้องการ ARC ติดตามจำนวนคุณสมบัติค่าคงที่และตัวแปรที่อ้างอิงถึงแต่ละอินสแตนซ์ของคลาส ARC จะไม่ยกเลิกการจัดสรรอินสแตนซ์ตราบใดที่ยังมีการอ้างอิงอย่างน้อยหนึ่งการอ้างอิงกับอินสแตนซ์นั้นอยู่

เพื่อให้แน่ใจว่าอินสแตนซ์จะไม่หายไปในขณะที่ยังต้องการ ARC ติดตามจำนวนคุณสมบัติค่าคงที่และตัวแปรที่อ้างอิงถึงแต่ละอินสแตนซ์ของคลาส ARC จะไม่ยกเลิกการจัดสรรอินสแตนซ์ตราบใดที่ยังมีการอ้างอิงอย่างน้อยหนึ่งการอ้างอิงกับอินสแตนซ์นั้นอยู่

หากต้องการทราบความแตกต่าง ระหว่างการรวบรวมขยะและ ARC: อ่านสิ่งนี้


0

ARC เป็นคุณสมบัติคอมไพเลอร์ที่ให้การจัดการหน่วยความจำอัตโนมัติของวัตถุ

แทนที่จะต้องจำเมื่อต้องใช้retain, releaseและautorelease ARC จะประเมินความต้องการอายุการใช้งานของวัตถุของคุณและแทรกการจัดการหน่วยความจำที่เหมาะสมสำหรับคุณโดยอัตโนมัติในเวลารวบรวม คอมไพเลอร์ยังสร้างวิธีการจัดสรรคืนที่เหมาะสมสำหรับคุณ

คอมไพเลอร์แทรกการretain/releaseโทรที่จำเป็นในเวลารวบรวม แต่การเรียกเหล่านั้นจะถูกดำเนินการในขณะทำงานเหมือนกับรหัสอื่น ๆ

แผนภาพต่อไปนี้จะช่วยให้คุณเข้าใจมากขึ้นว่า ARC ทำงานอย่างไร

ป้อนคำอธิบายรูปภาพที่นี่

ผู้ที่ยังใหม่ในการพัฒนา iOS และไม่มีประสบการณ์การทำงานกับ Objective C โปรดดูเอกสารประกอบของ Apple สำหรับคำแนะนำการเขียนโปรแกรมการจัดการหน่วยความจำขั้นสูงเพื่อความเข้าใจการจัดการหน่วยความจำได้ดีขึ้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.