การรั่วไหลของชนิดใดที่การอ้างอิงอัตโนมัติใน Objective-C ไม่ป้องกันหรือย่อให้เล็กสุด


235

ในแพลตฟอร์ม Mac และ iOS หน่วยความจำรั่วมักเกิดจากพอยน์เตอร์ที่ไม่ได้เผยแพร่ ตามเนื้อผ้ามันมีความสำคัญสูงสุดเสมอในการตรวจสอบ allocs ของคุณคัดลอกและเก็บเพื่อให้แน่ใจว่าแต่ละคนมีข้อความปล่อยที่สอดคล้องกัน

Toolchain ที่มาพร้อมกับ Xcode 4.2 แนะนำการนับจำนวนการอ้างอิงอัตโนมัติ (ARC) ด้วยตัวแปล LLVMเวอร์ชั่นล่าสุดซึ่งแก้ไขปัญหานี้ได้อย่างสมบูรณ์ มันเจ๋งมากและมันช่วยลดเวลาในการพัฒนาที่ไม่จำเป็นออกไปทางโลกและป้องกันการรั่วไหลของหน่วยความจำที่ไม่ประมาทจำนวนมากซึ่งง่ายต่อการแก้ไขด้วยการรักษา / ปล่อยสมดุลที่เหมาะสม แม้กระทั่งระบบพูลอัตโนมัติที่ต้องจัดการแตกต่างกันเมื่อคุณเปิดใช้งาน ARC สำหรับแอป Mac และ iOS (เพราะคุณไม่ควรจัดสรรNSAutoreleasePoolอีกต่อไป)

แต่การรั่วไหลของหน่วยความจำอื่น ๆมันไม่ได้ป้องกันไม่ให้ฉันยังต้องระวัง?

เป็นโบนัสอะไรคือความแตกต่างระหว่าง ARC บน Mac OS X และ iOS และการเก็บขยะบน Mac OS X

คำตอบ:


262

ปัญหาที่เกี่ยวข้องกับหน่วยความจำหลักที่คุณจะต้องทราบก็คือการรักษารอบ สิ่งนี้เกิดขึ้นเมื่อวัตถุหนึ่งมีตัวชี้ที่แข็งแกร่งไปยังวัตถุอื่น แต่วัตถุเป้าหมายนั้นมีตัวชี้ที่แข็งแกร่งกลับไปเป็นต้นฉบับ แม้ว่าการอ้างอิงอื่น ๆ ทั้งหมดไปยังวัตถุเหล่านี้จะถูกลบทิ้ง แต่การอ้างอิงเหล่านั้นจะยังคงค้างอยู่ซึ่งกันและกันและจะไม่ถูกเปิดเผย สิ่งนี้สามารถเกิดขึ้นได้ทางอ้อมด้วยโซ่ของวัตถุที่อาจมีห่วงโซ่สุดท้ายที่อ้างอิงกลับไปยังวัตถุก่อนหน้า

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

คุณจะใช้ตัวระบุเหล่านี้สำหรับสิ่งต่าง ๆ เช่นตัวแทนซึ่งคุณไม่ต้องการให้วัตถุเก็บรักษาตัวแทนและอาจนำไปสู่รอบ

คู่ของความกังวลที่เกี่ยวข้องกับหน่วยความจำที่สำคัญอีกประการหนึ่งที่มีการจัดการของมูลนิธิหลักวัตถุและจัดสรรหน่วยความจำโดยใช้ชนิดเช่นmalloc() char*ARC ไม่ได้จัดการประเภทเหล่านี้เฉพาะวัตถุ Objective-C ดังนั้นคุณจะต้องจัดการกับพวกเขาด้วยตัวเอง ประเภทของมูลนิธิแกนกลางอาจมีความซับซ้อนโดยเฉพาะอย่างยิ่งเพราะบางครั้งพวกเขาจำเป็นต้องเชื่อมโยงกับวัตถุ Objective-C ที่ตรงกันและในทางกลับกัน ซึ่งหมายความว่าจำเป็นต้องมีการควบคุมการถ่ายโอนกลับไปกลับมาจาก ARC เมื่อทำการเชื่อมต่อระหว่างชนิด CF และ Objective-C มีการเพิ่มคำหลักบางคำที่เกี่ยวข้องกับการเชื่อมโยงนี้และ Mike Ash มีคำอธิบายที่ดีเกี่ยวกับการเชื่อมโยงกรณีต่างๆในการเขียน ARC ที่มีความยาวมาก

นอกจากนี้ยังมีอีกหลายกรณีที่พบน้อยกว่า แต่ก็ยังมีปัญหาที่อาจเกิดขึ้นได้ซึ่งรายละเอียดที่ตีพิมพ์ระบุในรายละเอียด

พฤติกรรมใหม่ ๆ ส่วนใหญ่ซึ่งมีพื้นฐานอยู่บนการรักษาวัตถุรอบตัวตราบใดที่มีตัวชี้ที่ชัดเจนพวกมันก็คล้ายกับการรวบรวมขยะบน Mac อย่างไรก็ตามการสนับสนุนทางเทคนิคนั้นแตกต่างกันมาก รูปแบบของการจัดการหน่วยความจำนี้อาศัยกฎการรักษา / ปล่อยที่เข้มงวดซึ่งเราทุกคนต้องปฏิบัติตามใน Objective-C

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

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการรวบรวมขยะกับ ARC ให้ดูการตอบสนองที่น่าสนใจนี้โดย Chris Lattner ในรายชื่อผู้รับจดหมายของ Objective-Cซึ่งเขาแสดงข้อดีของ ARC มากกว่าการรวบรวมขยะ Objective-C 2.0 ฉันพบปัญหา GC หลายเรื่องที่เขาอธิบาย


2
ขอบคุณสำหรับคำตอบรายละเอียด ฉันมีปัญหาเดียวกันกับที่ฉันกำหนดผู้รับมอบสิทธิ์ภายใต้ _unsafe_unretained และทำให้แอปพลิเคชันของฉันทำงานล้มเหลวในภายหลังแก้ไขโดยเปลี่ยนเป็น strong แต่ตอนนี้มีหน่วยความจำรั่ว ดังนั้นฉันจึงเปลี่ยนเป็นอ่อนแอและทำงานเหมือนมีเสน่ห์
chathuram

@ichathura ว้าว! คุณช่วยฉันจากโคลน ARC ฉันพบข้อผิดพลาดเดียวกันเมื่อใช้ CMPopTipView
Nianliang

@BradLarson: "คุณไม่มีปัญหาในการหยุดหรือโปรไฟล์หน่วยความจำฟันเลื่อยที่พบบนแพลตฟอร์มที่เก็บขยะ" ฉันคาดหวังว่าโปรไฟล์หน่วยความจำจะหยุดและฟันเลื่อยแย่ลงจากการกู้คืนตามขอบเขตและประสิทธิภาพที่แย่กว่ามากจากการนับการอ้างอิงดังนั้นฉันต้องการดูการเปรียบเทียบจริง
Jon Harrop

แบรดเชื่อมโยงจากคริสแลตต์เนอร์เป็นตาย ฉันไม่ใช่ 100% แต่ฉันพบลิงค์อื่นนี้ ซึ่งฉันคิดว่าเป็นสิ่งที่คุณต้องการเชื่อมโยงไปยัง: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/…
Honey

1
@Honey - ขอบคุณที่ชี้ให้เห็น ลิงค์ที่คุณลิงค์แตกต่างกันเล็กน้อย แต่ฉันแทนที่ลิงค์ที่ตายแล้วด้วยข้อความต้นฉบับเวอร์ชันที่เก็บถาวร มันอยู่ในคลังรายการจดหมายซึ่งควรจะอยู่ที่ไหนสักแห่ง แต่ฉันจะดูเพื่อดูว่าฉันสามารถหาตำแหน่งใหม่ของพวกเขา
Brad Larson

14

ARC จะไม่ช่วยคุณด้วยหน่วยความจำที่ไม่ใช่ ObjC ตัวอย่างเช่นหากคุณมีmalloc()บางสิ่งคุณยังจำเป็นต้องfree()ใช้

ARC สามารถถูกหลอกได้performSelector:หากคอมไพเลอร์ไม่สามารถรู้ได้ว่าตัวเลือกคืออะไร (คอมไพเลอร์จะสร้างคำเตือนบนนั้น)

ARC จะสร้างรหัสตามระเบียบการตั้งชื่อ ObjC ดังนั้นหากคุณผสมรหัส ARC และ MRC คุณจะได้ผลลัพธ์ที่น่าแปลกใจหากรหัส MRC ไม่ได้ทำสิ่งที่คอมไพเลอร์คิดว่าชื่อสัญญา


7

ฉันประสบปัญหาหน่วยความจำรั่วในแอปพลิเคชันของฉันเนื่องจากปัญหา 4 ข้อต่อไปนี้:

  1. ไม่ทำให้ NSTimers ใช้ไม่ได้เมื่อยกเลิกการควบคุมมุมมอง
  2. ลืมที่จะลบผู้สังเกตการณ์ใด ๆ ไปยัง NSNotificationCenter เมื่อยกเลิกตัวควบคุมมุมมอง
  3. การอ้างอิงที่ดีต่อตนเองในบล็อก
  4. การใช้การอ้างอิงที่รัดกุมกับผู้รับมอบสิทธิ์ในคุณสมบัติตัวควบคุมมุมมอง

โชคดีที่ฉันเจอบล็อกโพสต์ต่อไปนี้และสามารถแก้ไขได้: http://www.reigndesign.com/blog/debugging-retain-Mp3-in-objective-c-four-ossible-culprits/


0

ARC จะไม่จัดการประเภท CoreFoundation คุณสามารถ 'สะพาน' พวกเขา (ใช้CFBridgingRelease()) แต่ถ้าคุณจะใช้มันเป็นวัตถุ Objective-C / Cocoa โปรดทราบว่า CFBridgingRelease เพียงลด CoreFoundation ยังคงนับ 1 และย้ายไปยัง ARC ของ Objective-C


0

Xcode 9 เป็นเครื่องมือที่ยอดเยี่ยมสำหรับการค้นหาปัญหาประเภทนั้น เรียกว่า: " Debug Memory Graph " การใช้มันคุณสามารถค้นหาวัตถุที่รั่วไหลของคุณตามประเภทของชั้นเรียนและคุณสามารถเห็นได้อย่างชัดเจนว่าใครเป็นผู้อ้างอิงที่ดีถึงมันโดยการปล่อยมันออกมาจากที่นั่นเพื่อแก้ปัญหาของคุณ นอกจากนี้ยังตรวจพบวงจรหน่วยความจำ

ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีใช้งาน

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