บางคนสามารถอธิบายสั้น ๆ ว่า ARC ทำงานอย่างไร ฉันรู้ว่ามันแตกต่างจาก Garbage Collection แต่ฉันแค่สงสัยว่ามันทำงานอย่างไร
นอกจากนี้หาก ARC ทำในสิ่งที่ GC ทำโดยไม่มีการขัดขวางประสิทธิภาพแล้วเหตุใด Java จึงใช้ GC ทำไมมันไม่ใช้ ARC เช่นกัน?
บางคนสามารถอธิบายสั้น ๆ ว่า ARC ทำงานอย่างไร ฉันรู้ว่ามันแตกต่างจาก Garbage Collection แต่ฉันแค่สงสัยว่ามันทำงานอย่างไร
นอกจากนี้หาก ARC ทำในสิ่งที่ GC ทำโดยไม่มีการขัดขวางประสิทธิภาพแล้วเหตุใด Java จึงใช้ GC ทำไมมันไม่ใช้ ARC เช่นกัน?
คำตอบ:
นักพัฒนาใหม่ทุกคนที่มาถึง 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
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 จะทำงานได้ดีขึ้นมากสำหรับรูปแบบการใช้งานทั่วไปจำนวนมาก แต่สำหรับรูปแบบการใช้งาน (ถูกต้อง!) เพียงเล็กน้อยมันจะล้มลงและตายไป
foo = nil
คือการทำลายตนเองของวงจรเช่น
มายากล
แต่โดยเฉพาะอย่างยิ่ง ARC ทำงานโดยทำสิ่งที่คุณจะทำกับรหัสของคุณ (มีความแตกต่างเล็กน้อย) ARC เป็นเทคโนโลยีเวลารวบรวมซึ่งแตกต่างจาก GC ซึ่งเป็นรันไทม์และจะส่งผลกระทบต่อประสิทธิภาพการทำงานของคุณ ARC จะติดตามการอ้างอิงไปยังวัตถุสำหรับคุณและสังเคราะห์วิธีการเก็บรักษา / ปล่อย / การเลิกอัตโนมัติตามกฎปกติ เพราะอาร์คนี้สามารถปล่อยสิ่งต่าง ๆ ได้ทันทีที่พวกเขาไม่ต้องการอีกต่อไปแทนที่จะทิ้งมันลงในสระว่ายน้ำอัตโนมัติที่หมดจดเพื่อประโยชน์ในการประชุม
การปรับปรุงอื่น ๆ รวมถึงการอ้างอิงศูนย์ที่อ่อนแอการคัดลอกบล็อกอัตโนมัติไปยังฮีปการเร่งความเร็วทั่วกระดาน (6x สำหรับพูลการกู้คืนอัตโนมัติ!)
การสนทนาโดยละเอียดเพิ่มเติมเกี่ยวกับวิธีการทำงานทั้งหมดนี้พบได้ในLLVM เอกสารบน ARC
มันแตกต่างอย่างมากจากการเก็บขยะ คุณเคยเห็นคำเตือนที่บอกคุณหรือไม่ว่าคุณกำลังรั่วสิ่งของต่าง ๆ ? ข้อความเหล่านั้นบอกคุณในบรรทัดที่คุณจัดสรรวัตถุ สิ่งนี้ได้ถูกดำเนินการไปอีกขั้นหนึ่งและตอนนี้สามารถแทรกretain
/ release
แถลงการณ์ในสถานที่ที่เหมาะสมดีกว่าโปรแกรมเมอร์ส่วนใหญ่เกือบ 100% ของเวลา ในบางครั้งมีบางสิ่งที่แปลกประหลาดของวัตถุที่ถูกเก็บไว้ซึ่งคุณต้องการช่วย
อธิบายได้ดีมากโดยเอกสารประกอบสำหรับนักพัฒนาของ Apple อ่าน"วิธีการทำงานของ ARC"
เพื่อให้แน่ใจว่าอินสแตนซ์จะไม่หายไปในขณะที่ยังต้องการ ARC ติดตามจำนวนคุณสมบัติค่าคงที่และตัวแปรที่อ้างอิงถึงแต่ละอินสแตนซ์ของคลาส ARC จะไม่ยกเลิกการจัดสรรอินสแตนซ์ตราบใดที่ยังมีการอ้างอิงอย่างน้อยหนึ่งการอ้างอิงกับอินสแตนซ์นั้นอยู่
เพื่อให้แน่ใจว่าอินสแตนซ์จะไม่หายไปในขณะที่ยังต้องการ ARC ติดตามจำนวนคุณสมบัติค่าคงที่และตัวแปรที่อ้างอิงถึงแต่ละอินสแตนซ์ของคลาส ARC จะไม่ยกเลิกการจัดสรรอินสแตนซ์ตราบใดที่ยังมีการอ้างอิงอย่างน้อยหนึ่งการอ้างอิงกับอินสแตนซ์นั้นอยู่
หากต้องการทราบความแตกต่าง ระหว่างการรวบรวมขยะและ ARC: อ่านสิ่งนี้
ARC เป็นคุณสมบัติคอมไพเลอร์ที่ให้การจัดการหน่วยความจำอัตโนมัติของวัตถุ
แทนที่จะต้องจำเมื่อต้องใช้retain, release
และautorelease
ARC จะประเมินความต้องการอายุการใช้งานของวัตถุของคุณและแทรกการจัดการหน่วยความจำที่เหมาะสมสำหรับคุณโดยอัตโนมัติในเวลารวบรวม คอมไพเลอร์ยังสร้างวิธีการจัดสรรคืนที่เหมาะสมสำหรับคุณ
คอมไพเลอร์แทรกการretain/release
โทรที่จำเป็นในเวลารวบรวม แต่การเรียกเหล่านั้นจะถูกดำเนินการในขณะทำงานเหมือนกับรหัสอื่น ๆ
แผนภาพต่อไปนี้จะช่วยให้คุณเข้าใจมากขึ้นว่า ARC ทำงานอย่างไร
ผู้ที่ยังใหม่ในการพัฒนา iOS และไม่มีประสบการณ์การทำงานกับ Objective C โปรดดูเอกสารประกอบของ Apple สำหรับคำแนะนำการเขียนโปรแกรมการจัดการหน่วยความจำขั้นสูงเพื่อความเข้าใจการจัดการหน่วยความจำได้ดีขึ้น