การจัดการหน่วยความจํา

คำถามแรกที่นักพัฒนาซอฟต์แวร์ Java ส่วนใหญ่มีอยู่คือ การจัดการหน่วยความจำโดย J2ObjC นั้นทำอย่างไร เนื่องจาก Java มีการรวบรวม Garbage แต่ Objective-C ไม่ได้ทำโดยค่าเริ่มต้น สิ่งที่ iOS มีการจัดการหน่วยความจำ 2 วิธี ได้แก่ การนับการอ้างอิง และการนับการอ้างอิงอัตโนมัติ (ARC)

J2ObjC สร้างโค้ดการจัดการหน่วยความจำที่แตกต่างกัน โดยขึ้นอยู่กับวิธีการที่เลือก แปลโดยใช้ตัวเลือก -use-arc เพื่อสร้างโค้ดที่ใช้ ARC โดยค่าเริ่มต้น จะใช้การนับข้อมูลอ้างอิงด้วยตนเอง

การนับข้อมูลอ้างอิง

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

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

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

เนื่องจากนักพัฒนาซอฟต์แวร์ต้องแบกรับภาระการจัดการหน่วยความจำ วิธีการนับการอ้างอิงจึงรั่วไหลง่าย อย่างไรก็ตาม Apple ขอแนะนำแนวทางปฏิบัติที่ดีที่สุดบางประการเพื่อลดปัญหานี้ซึ่ง J2ObjC ใช้

นอกจากนี้ยังมีรันไทม์และการสนับสนุนเครื่องมือเพื่อตรวจหาการรั่วไหลของหน่วยความจำอีกด้วย รันไทม์ของ Objective-C จะรายงานการรั่วไหลที่ตรวจพบเมื่อแอปพลิเคชันลาออก ซึ่งเป็นเหตุผลหนึ่งที่ J2ObjC แปลการทดสอบ JUnit เป็นไบนารีที่สั่งการได้ Xcode ใช้ Clang และคอมไพเลอร์ดังกล่าวมีการวิเคราะห์เชิงสถิติที่ยอดเยี่ยมสำหรับปัญหาหน่วยความจำ ซึ่ง Xcode มีให้ใช้งานผ่านคำสั่งวิเคราะห์

การนับการอ้างอิงอัตโนมัติ (ARC)

ARC เป็นวิธีการจัดการหน่วยความจำที่ Apple แนะนำ ย้ายความรับผิดชอบในการนับข้อมูลอ้างอิงไปยังคอมไพเลอร์ ซึ่งจะเพิ่มวิธีการเก็บรักษา การเผยแพร่ และการเผยแพร่อัตโนมัติที่เหมาะสมระหว่างการรวบรวม ซึ่ง ARC รองรับการอ้างอิงที่ไม่รัดกุมสำหรับอุปกรณ์ที่ใช้ iOS 5 ขึ้นไป

เราขอแนะนำให้โปรเจ็กต์ใช้ ARC สำหรับโค้ดที่แปลแล้ว Transpiled โค้ด Objective-C จะเหมือนกับโค้ด Objective-C ที่เขียนด้วยลายมือ การใช้ ARC จะช่วยให้นักพัฒนาซอฟต์แวร์หลีกเลี่ยงข้อผิดพลาดที่พบบ่อยเกี่ยวกับหน่วยความจำ เช่น การปล่อยมากเกินไปหรืออ้างอิงน้อยเกินไป ทำให้การจัดการหน่วยความจำเป็นเรื่องง่ายขึ้นและเกิดข้อผิดพลาดน้อยลง

โปรดทราบว่า ARC ไม่ได้ไม่ปลอดภัยโดยค่าเริ่มต้น กล่าวอย่างเจาะจงคือ หน่วยความจำรั่วไหลเมื่อมีการมีข้อยกเว้น เนื่องจากข้อยกเว้นพบได้บ่อยใน Java และมักจะกู้คืนได้ ปัญหานี้จึงอาจทำให้เกิดปัญหา การใช้ -fobjc-arc-exceptions เมื่อคอมไพล์ด้วยเส้นโค้งจะแก้ไขการรั่วไหลโดยมีต้นทุนด้านประสิทธิภาพที่ยอมรับได้พอสมควร

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

การอ้างอิงที่ไม่รัดกุม

คุณอาจใส่คำอธิบายประกอบในช่องด้วย com.google.devtools.j2objc.Weak ได้ ซึ่งเครื่องมือแปลงข้อมูลจะใช้ในการสร้างช่องที่เป็นไปตามความหมายของการอ้างอิงที่ไม่รัดกุมของ Objective-C เมื่อใช้การนับข้อมูลอ้างอิง หมายความว่าไม่มีการเก็บช่องนี้ไว้เมื่อเริ่มต้น และช่องจะถูกปล่อยโดยอัตโนมัติเมื่อปล่อยอินสแตนซ์ที่มี หากใช้ ARC ช่องที่ไม่รัดกุมจะมีคำอธิบายประกอบ __unsafe_unretained และระบบจะประกาศพร็อพเพอร์ตี้ที่เกี่ยวข้องว่าอ่อน

ในบางกรณี อินสแตนซ์คลาสภายในจะอยู่ในวงจรอ้างอิงโดยมีอินสแตนซ์ภายนอก ในที่นี้ จะมีการใช้คำอธิบายประกอบ com.google.devtools.j2objc.WeakOuter เพื่อทำเครื่องหมายคลาสภายใน ดังนั้นระบบจะดําเนินการตามการอ้างอิงคลาสภายนอกตามที่อธิบายไว้ข้างต้น ช่องอื่นๆ ในคลาสภายในจะไม่ได้รับผลกระทบจากคำอธิบายประกอบนี้

J2ObjC ยังรองรับคลาส WeakReference ด้วย ดังนั้นโค้ด Java ที่ใช้ก็จะทำงานในลักษณะเดียวกันเมื่อแปล โปรดทราบว่าโดยธรรมชาติแล้ว WeakReference ไม่ได้กำหนดใน JVM อยู่แล้ว แอปพลิเคชันที่ต้องการหลีกเลี่ยงการรั่วไหลของหน่วยความจำขณะที่คงความสามารถในการคาดการณ์ควรเลือกใช้ @Weak และ @WeakOuter แทน

เครื่องมือจัดการหน่วยความจำ