กลุ่ม NSAutoreleasePool autorelease ทำงานอย่างไร


95

ที่ผมเข้าใจสิ่งที่สร้างขึ้นกับalloc , ใหม่หรือสำเนาความต้องการที่จะได้รับการปล่อยตัวด้วยตนเอง ตัวอย่างเช่น:

int main(void) {
    NSString *string;
    string = [[NSString alloc] init];
    /* use the string */
    [string release];
}

คำถามของฉันมันจะไม่ถูกต้องเท่านี้หรือ:

int main(void) {
    NSAutoreleasePool *pool;
    pool = [[NSAutoreleasePool alloc] init];
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
    [pool drain];
}

คำตอบ:


68

ใช่ข้อมูลโค้ดที่สองของคุณใช้ได้อย่างสมบูรณ์

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

กลุ่มการปล่อยอัตโนมัติเป็นเพียงความสะดวกสบายที่ช่วยให้คุณสามารถเลื่อนการส่ง - ปล่อยออกไปจนกว่า "ในภายหลัง" "ในภายหลัง" อาจเกิดขึ้นได้ในหลายที่ แต่สิ่งที่พบบ่อยที่สุดในแอป Cocoa GUI คือเมื่อสิ้นสุดรอบการวนรอบการทำงานปัจจุบัน


5
จุดสิ้นสุดของวัฏจักรการรันปัจจุบันอยู่ที่ไหนถ้าฉันไม่มีลูป
ขอบคุณ

24
"ด้านนอกสุด" ไม่ควรเป็น "ด้านในสุด" ใช่หรือไม่?
Mike Weller

an objectan object that is a subclass of NSObject or NSProxy and doesn't override -autoreleaseควรจะเป็น

1
แก้ไข: เปลี่ยนด้านนอกสุดเป็นด้านในสุด
ฤษ

1
สำคัญ: หากคุณใช้ Automatic Reference Counting (ARC) คุณจะไม่สามารถใช้กลุ่ม autorelease โดยตรง คุณใช้บล็อก @autoreleasepool แทน จากdeveloper.apple.com/library/mac/#documentation/Cocoa/Reference/…
Md Mahbubur Rahman

37

NSAutoreleasePool: ท่อระบายน้ำเทียบกับการปล่อย

เนื่องจากฟังก์ชั่นdrainและreleaseดูเหมือนจะก่อให้เกิดความสับสนจึงควรชี้แจงที่นี่ (แม้ว่าจะครอบคลุมอยู่ในเอกสารประกอบ ... )

พูดอย่างเคร่งครัดจากมุมมองของภาพใหญ่drainคือไม่เทียบเท่ากับrelease:

ในสภาพแวดล้อมที่นับการอ้างอิงdrainจะดำเนินการเช่นเดียวกันreleaseดังนั้นทั้งสองจึงเทียบเท่ากัน จะเน้นวิธีนี้คุณจะไม่รั่วสระว่ายน้ำถ้าคุณใช้มากกว่าdrainrelease

ในสภาพแวดล้อมที่เก็บขยะreleaseเป็นสิ่งที่ไม่ควรทำ ดังนั้นจึงไม่มีผล drainในทางกลับกันมีคำใบ้ให้กับนักสะสมว่าควร "เก็บถ้าจำเป็น" ดังนั้นในสภาพแวดล้อมที่มีการเก็บขยะการใช้drainจะช่วยให้ระบบรวบรวมข้อมูลสมดุล


4
โดยพื้นฐานแล้วเป็นไปไม่ได้ที่จะ 'รั่วไหล' กNSAutoreleasePool. เนื่องจากสระว่ายน้ำทำงานเหมือนกองซ้อน การอินสแตนซ์พูลจะผลักพูลนั้นไปที่ด้านบนสุดของสแต็กพูลรีลีสอัตโนมัติเธรดนั้น -releaseทำให้พูลนั้นโผล่ขึ้นมาจากสแต็กและพูลใด ๆ ที่ถูกดันไปด้านบน แต่ไม่ว่าด้วยเหตุผลใดก็ตาม
johne

7
สิ่งนี้เกี่ยวข้องกับสิ่งที่ฉันเขียนในทางใด
mmalc

2
ฉันชอบวิธีที่เขาใช้เวลาในการกล้าได้กล้าเสียและ SNAP!
Billy Gray

17

ตามที่ได้ระบุไว้แล้วข้อมูลโค้ดที่สองของคุณถูกต้อง

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

int main(void) {
  @autoreleasepool {
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
  }
}

ในตัวอย่างด้านบนโปรดสังเกตบล็อก@autoreleasepool นี้เป็นเอกสารที่นี่


2
โปรดทราบว่า ARC ไม่อนุญาตให้ปล่อยอัตโนมัติ
dmirkitanov

1
เพื่อความชัดเจนเราต้องใช้@autoreleasepoolบล็อกกับ ARC
Simon

7

ไม่คุณคิดผิด เอกสารระบุชัดเจนว่าภายใต้ non-GC -drain เทียบเท่ากับ -release ซึ่งหมายความว่า NSAutoreleasePool จะไม่รั่วไหล


ฉันสงสัยว่าทำไม Xcode ถึงสร้างโค้ดด้วย -drain ถ้าเป็นเช่นนั้น ฉันใช้ -drain เพราะฉันคิดว่ามันเทียบเท่ากับ -release ตามรหัสที่สร้างโดย Xcode
James Sumners

1
โดยพื้นฐานแล้วเป็นไปไม่ได้ที่จะ 'รั่วไหล' a NSAutoreleasePool: developer.apple.com/mac/library/documentation/Cocoa/Conceptual/…
johne

0

สิ่งที่ฉันอ่านจาก Apple: "ในตอนท้ายของบล็อกพูลการปล่อยอัตโนมัติออบเจ็กต์ที่ได้รับข้อความปลดล็อกอัตโนมัติภายในบล็อกจะถูกส่งข้อความปล่อย - วัตถุจะได้รับข้อความเผยแพร่ทุกครั้งที่มีการส่งข้อความปลดอัตโนมัติภายในบล็อก "

https://developer.apple.com/library/mac/documentation/cocoa/conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html


0

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


-2

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

ท่อระบายน้ำ

ในสภาพแวดล้อมที่รวบรวมขยะจะทริกเกอร์การรวบรวมขยะหากหน่วยความจำที่จัดสรรตั้งแต่คอลเล็กชันล่าสุดมีค่ามากกว่าเกณฑ์ปัจจุบัน มิฉะนั้นจะทำงานเป็นการปลดปล่อย ... ในสภาพแวดล้อมที่เก็บขยะในที่สุดวิธีนี้เรียกobjc_collect_if_neededว่า

มิฉะนั้นจะคล้ายกับการ-releaseทำงานภายใต้ไม่ใช่ GC ใช่ ตามที่คนอื่น ๆ ระบุไว้ว่า-releaseเป็น no-op ภายใต้ GC ดังนั้นวิธีเดียวที่จะทำให้แน่ใจว่าพูลทำงานอย่างถูกต้องภายใต้ GC นั้นผ่าน-drainและ-drainภายใต้การทำงานที่ไม่ใช่ GC จะทำงานเหมือนกับ-releaseภายใต้ GC ที่ไม่ใช่ GC และเนื้อหาจะสื่อสารการทำงานของมันได้ชัดเจนกว่า ดี.

ฉันควรชี้ให้เห็นว่าคำสั่งของคุณ "สิ่งที่เรียกด้วย new, การจัดสรรหรือการเริ่มต้น" ไม่ควรรวม "init" (แต่ควรรวมถึง "สำเนา") เนื่องจาก "init" ไม่ได้จัดสรรหน่วยความจำ แต่เป็นการตั้งค่าวัตถุเท่านั้น (ตัวสร้าง แฟชั่น). หากคุณได้รับออบเจ็กต์ที่จัดสรรและฟังก์ชันของคุณเรียกว่า init เท่านั้นคุณจะไม่ปล่อยมัน:

- (void)func:(NSObject*)allocd_but_not_init
{
    [allocd_but_not_init init];
}

นั่นไม่ได้ใช้หน่วยความจำมากกว่าที่คุณเริ่มต้นไปแล้ว (สมมติว่า init ไม่ได้สร้างอินสแตนซ์ออบเจ็กต์ แต่คุณจะไม่รับผิดชอบต่อสิ่งเหล่านั้น)


ฉันไม่สบายใจที่จะปล่อยให้คำตอบนี้เป็นที่ยอมรับเมื่อข้อมูลของคุณเกี่ยวกับท่อระบายน้ำไม่ถูกต้อง โปรดดูdeveloper.apple.com/documentation/Cocoa/Reference/Foundation/… Update แล้วฉันจะยอมรับอีกครั้ง
James Sumners

คำตอบที่ไม่ถูกต้องคืออะไร ในสภาพแวดล้อมที่รวบรวมขยะ (ตามที่ระบุไว้) ท่อระบายน้ำไม่ได้ลบ AutoReleasePool ดังนั้นคุณจะรั่วไหลหน่วยความจำเว้นแต่คุณจะใช้รีลีส คำพูดที่ฉันระบุนั้นตรงจากปากม้าเอกสารเกี่ยวกับท่อระบายน้ำ
Loren Segal

1
ลอเรน: ภายใต้ GC - [NSAutoreleasePool drain] จะเรียกคอลเล็กชัน -retain, -release และ -autorelease ล้วนถูกละเลยโดยผู้รวบรวม นั่นเป็นเหตุผลที่ใช้ -drain ในกลุ่มการปล่อยอัตโนมัติภายใต้ GC
Chris Hanson

ในเอกสารสำหรับ 'ท่อระบายน้ำ': ในสภาพแวดล้อมหน่วยความจำที่มีการจัดการสิ่งนี้จะทำงานเหมือนกับการเรียกรีลีส ดังนั้นคุณจะไม่รั่วไหลของหน่วยความจำหากคุณใช้ 'ท่อระบายน้ำ' แทนการปล่อย
mmalc

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