พูลการกู้คืนอัตโนมัติจำเป็นต้องมีเพื่อส่งคืนวัตถุที่สร้างขึ้นใหม่จากวิธีการ เช่นพิจารณารหัสชิ้นนี้:
- (NSString *)messageOfTheDay {
return [[NSString alloc] initWithFormat:@"Hello %@!", self.username];
}
สตริงที่สร้างขึ้นในวิธีการจะมีจำนวนการเก็บรักษาไว้หนึ่ง ตอนนี้ใครจะเป็นคนที่รักษาสมดุลที่นับด้วยการเปิดตัว?
วิธีการของตัวเอง? เป็นไปไม่ได้มันจะต้องส่งคืนวัตถุที่สร้างขึ้นดังนั้นจึงต้องไม่ปล่อยมันก่อนที่จะส่งคืน
วิธีการโทร? ผู้เรียกไม่ได้คาดหวังว่าจะดึงวัตถุที่ต้องการปล่อยชื่อวิธีไม่ได้หมายความว่ามีการสร้างวัตถุใหม่เพียงแค่บอกว่าวัตถุจะถูกส่งกลับและวัตถุที่คืนนี้อาจจะเป็นคนใหม่ที่ต้องการปล่อย แต่อาจเป็น เป็นคนที่มีอยู่แล้วที่ไม่ดี สิ่งที่วิธีการส่งคืนอาจขึ้นอยู่กับสถานะภายในบางอย่างดังนั้นผู้เรียกไม่สามารถรู้ได้ว่าจะต้องปล่อยวัตถุนั้นหรือไม่และไม่ควรสนใจ
หากผู้เรียกต้องปล่อยวัตถุที่ถูกส่งกลับทั้งหมดโดยการประชุมวัตถุทุกชิ้นที่ไม่ได้สร้างขึ้นใหม่จะต้องถูกเก็บไว้ก่อนที่จะส่งคืนจากวิธีการและจะต้องได้รับการปล่อยตัวจากผู้โทรเมื่อมันหลุดออกนอกขอบเขต มันกลับมาอีกครั้ง สิ่งนี้จะไม่มีประสิทธิภาพสูงในหลาย ๆ กรณีเนื่องจากหนึ่งสามารถหลีกเลี่ยงการเปลี่ยนแปลงการเก็บรักษาไว้ในหลาย ๆ กรณีได้หากผู้เรียกไม่ปล่อยวัตถุที่ถูกส่งคืนเสมอ
นั่นเป็นเหตุผลที่มีพูลการถอนการปิดอัตโนมัติดังนั้นวิธีการแรกจะกลายเป็นจริง
- (NSString *)messageOfTheDay {
NSString * res = [[NSString alloc] initWithFormat:@"Hello %@!", self.username];
return [res autorelease];
}
การเรียกร้อง autorelease
หาวัตถุเพิ่มไปยังสระว่ายน้ำอัตโนมัติ แต่นั่นหมายความว่าอะไรการเพิ่มวัตถุลงในสระน้ำอัตโนมัติ มันหมายถึงการบอกระบบของคุณ " ฉันต้องการให้คุณปล่อยวัตถุนั้นให้ฉัน แต่ในเวลาต่อมาไม่ใช่ตอนนี้มันมีจำนวนการเก็บรักษาที่ต้องมีความสมดุลโดยการเปิดตัวมิฉะนั้นหน่วยความจำจะรั่วไหล ในขณะนี้เนื่องจากฉันต้องการให้วัตถุมีชีวิตอยู่นอกเหนือขอบเขตปัจจุบันของฉันและผู้เรียกของฉันจะไม่ทำเพื่อฉันอย่างใดอย่างหนึ่งก็ไม่มีความรู้ว่าสิ่งนี้จะต้องทำดังนั้นเพิ่มลงในสระว่ายน้ำของคุณและเมื่อคุณทำความสะอาด สระน้ำทำความสะอาดวัตถุของฉันด้วย "
ด้วย ARC คอมไพเลอร์จะตัดสินว่าคุณจะเก็บรักษาวัตถุเมื่อใดเมื่อไหร่ที่จะปล่อยวัตถุและเมื่อเพิ่มวัตถุลงในสระว่ายน้ำอัตโนมัติ แต่มันยังต้องการการมีสระว่ายน้ำอัตโนมัติที่จะกลับวัตถุที่สร้างขึ้นใหม่โดยไม่รั่วหน่วยความจำ Apple เพิ่งปรับการปรับแต่งที่ดีให้กับโค้ดที่สร้างขึ้นซึ่งบางครั้งจะกำจัดพูลการหยุดอัตโนมัติในระหว่างรันไทม์ การปรับให้เหมาะสมเหล่านี้ต้องการทั้งผู้โทรและผู้ใช้กำลังใช้ ARC (โปรดจำไว้ว่าการผสม ARC และไม่ใช่ ARC นั้นถูกกฎหมายและรองรับอย่างเป็นทางการ) และหากเป็นกรณีจริงสามารถรู้ได้เฉพาะที่รันไทม์
พิจารณารหัส ARC นี้:
// Callee
- (SomeObject *)getSomeObject {
return [[SomeObject alloc] init];
}
// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];
รหัสที่ระบบสร้างสามารถทำงานได้เหมือนรหัสต่อไปนี้ (ซึ่งเป็นรุ่นที่ปลอดภัยที่ช่วยให้คุณสามารถผสมรหัส ARC และรหัสที่ไม่ใช่ ARC ได้อย่างอิสระ):
// Callee
- (SomeObject *)getSomeObject {
return [[[SomeObject alloc] init] autorelease];
}
// Caller
SomeObject * obj = [[self getSomeObject] retain];
[obj doStuff];
[obj release];
(โปรดทราบว่าการเก็บรักษา / การปล่อยในผู้โทรเป็นเพียงการรักษาความปลอดภัยการป้องกันมันไม่จำเป็นต้องเคร่งครัดรหัสจะถูกต้องสมบูรณ์แบบโดยไม่มีมัน)
หรือมันสามารถทำตัวเหมือนรหัสนี้ในกรณีที่ตรวจพบว่าทั้งคู่ใช้ ARC ในขณะใช้งานจริง:
// Callee
- (SomeObject *)getSomeObject {
return [[SomeObject alloc] init];
}
// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];
[obj release];
อย่างที่คุณเห็น Apple กำจัด atuorelease ดังนั้นการปล่อยวัตถุล่าช้าเมื่อสระถูกทำลายรวมถึงความปลอดภัย หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับวิธีการที่เป็นไปได้และสิ่งที่เกิดขึ้นจริงเบื้องหลังลองดูโพสต์บล็อกนี้
ตอนนี้คำถามที่เกิดขึ้นจริง: ทำไมหนึ่งจะใช้@autoreleasepool
?
สำหรับนักพัฒนาส่วนใหญ่มีเพียงเหตุผลเดียวที่เหลืออยู่วันนี้สำหรับการใช้งานโครงสร้างนี้ในรหัสของพวกเขา เช่นพิจารณาลูปนี้:
for (int i = 0; i < 1000000; i++) {
// ... code ...
TempObject * to = [TempObject tempObjectForData:...];
// ... do something with to ...
}
สมมติว่าทุกการเรียกไปยังtempObjectForData
อาจสร้างใหม่TempObject
ที่ส่งคืนอัตโนมัติ for-loop จะสร้างหนึ่งล้านของวัตถุ temp เหล่านี้ซึ่งถูกรวบรวมทั้งหมดใน autoreleasepool ปัจจุบันและเพียงครั้งเดียวที่สระว่ายน้ำถูกทำลายวัตถุ temp ทั้งหมดจะถูกทำลายเช่นกัน คุณจะมีวัตถุชั่วคราวหนึ่งล้านในหน่วยความจำ
หากคุณเขียนรหัสเช่นนี้แทน:
for (int i = 0; i < 1000000; i++) @autoreleasepool {
// ... code ...
TempObject * to = [TempObject tempObjectForData:...];
// ... do something with to ...
}
จากนั้นมีการสร้างพูลใหม่ทุกครั้งที่ for-loop ทำงานและถูกทำลายเมื่อสิ้นสุดการวนซ้ำแต่ละครั้ง ด้วยวิธีนี้วัตถุ temp ส่วนใหญ่นั้นจะวนอยู่ในหน่วยความจำตลอดเวลาแม้จะมีลูปที่ทำงานหนึ่งล้านครั้งก็ตาม
ในอดีตคุณจะต้องจัดการ autoreleasepools ด้วยตนเองเมื่อจัดการกับเธรด (เช่นการใช้NSThread
) เนื่องจากเฉพาะเธรดหลักเท่านั้นที่มีพูล autorelease โดยอัตโนมัติสำหรับแอพ Cocoa / UIKit แต่นี่เป็นมรดกที่ค่อนข้างมากในวันนี้เพราะวันนี้คุณอาจจะไม่ใช้กระทู้เริ่มต้น คุณจะใช้ GCD DispatchQueue
หรือNSOperationQueue
ทั้งสองอย่างนี้จัดการกับกลุ่มการ autorelease ระดับบนให้คุณสร้างขึ้นก่อนที่จะรันบล็อก / งานและทำลายเมื่อทำเสร็จแล้ว