ในสถานการณ์ใดบ้างที่เราต้องเขียน __autoreleasing ความเป็นเจ้าของคุณสมบัติภายใต้ ARC


118

ฉันกำลังพยายามไขปริศนาให้เสร็จ

__strongเป็นค่าเริ่มต้นสำหรับพอยน์เตอร์อ็อบเจ็กต์ Objective-C ที่เก็บรักษาได้เช่น NSObject, NSString และอื่น ๆ ซึ่งเป็นการอ้างอิงที่ชัดเจน ARC จะปรับสมดุลกับ-releaseจุดสิ้นสุดของขอบเขต

__unsafe_unretainedเท่ากับวิธีเก่า ใช้สำหรับตัวชี้ที่อ่อนแอโดยไม่ยึดวัตถุที่ยึดได้

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

แต่สิ่งที่__autoreleasingดีสำหรับ? ฉันมีช่วงเวลาที่ยากลำบากในการค้นหาตัวอย่างที่ใช้ได้จริงเกี่ยวกับเวลาที่ฉันต้องใช้คุณสมบัตินี้ ฉันเชื่อว่าเป็นเพียงฟังก์ชันและวิธีการที่คาดว่าจะมีตัวชี้ตัวชี้เช่น:

- (BOOL)save:(NSError**);

หรือ

NSError *error = nil;
[database save:&error];

ซึ่งภายใต้ ARC จะต้องประกาศด้วยวิธีนี้:

- (BOOL)save:(NSError* __autoreleasing *);

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

นอกจากนี้ยังอาจจะมีสถานการณ์อื่น ๆ __autoreleasingที่ฉันจะต้องพึ่งพา


1
ฉันมีคำถามเดียวกันนี้และคำตอบด้านล่างไม่น่าเชื่อโดยสิ้นเชิง ... ตัวอย่างเช่นทำไมระบบไม่ให้อินเทอร์เฟซที่ใช้อาร์กิวเมนต์ NSError ** ที่ประกาศด้วยมัณฑนากร __autoreleasing เช่นคุณและการเปลี่ยนไปใช้ Arc Release Notes ควรจะเป็น? เช่นกิจวัตรเหล่านี้ใด ๆ ใน NSFileManager.h ??
พ่อ

คำตอบ:


67

คุณถูก. ตามเอกสารอย่างเป็นทางการอธิบาย:

__autoreleasing เพื่อแสดงถึงอาร์กิวเมนต์ที่ส่งผ่านโดยการอ้างอิง (id *) และจะปล่อยกลับโดยอัตโนมัติ

ทั้งหมดนี้จะมีการอธิบายอย่างดีในคู่มือการเปลี่ยนแปลง ARC

ในตัวอย่าง NSError ของคุณการประกาศหมายถึง__strongโดยปริยาย:

NSError * e = nil;

จะเปลี่ยนเป็น:

NSError * __strong error = nil;

เมื่อคุณเรียกsaveวิธีการของคุณ:

- ( BOOL )save: ( NSError * __autoreleasing * );

จากนั้นคอมไพเลอร์จะต้องสร้างตัวแปรชั่วคราวโดยกำหนดไว้ที่__autoreleasing. ดังนั้น:

NSError * error = nil;
[ database save: &error ];

จะเปลี่ยนเป็น:

NSError * __strong error = nil;
NSError * __autoreleasing tmpError = error;
[ database save: &tmpError ];
error = tmpError;

คุณสามารถหลีกเลี่ยงสิ่งนี้ได้โดยการประกาศวัตถุข้อผิดพลาดเป็น__autoreleasingโดยตรง


3
ไม่__autoreleasingใช้สำหรับอาร์กิวเมนต์ที่ส่งผ่านการอ้างอิงเท่านั้น นี่เป็นกรณีพิเศษเนื่องจากคุณมีตัวชี้ไปยังตัวชี้ของวัตถุ นั่นไม่ใช่กรณีของสิ่งต่างๆเช่นตัวสร้างความสะดวกสบายเนื่องจากจะส่งกลับตัวชี้ไปที่วัตถุเท่านั้นและเมื่อ ARC จัดการโดยอัตโนมัติ
Macmade

7
เหตุใด __autoreleasing qualifier จึงถูกวางไว้ระหว่างดวงดาวไม่ใช่แค่ตรงหน้า NSError ** สิ่งนี้ดูแปลกสำหรับฉันเนื่องจากประเภทคือ NSError ** หรือเป็นเพราะสิ่งนี้พยายามบ่งชี้ว่าตัวชี้ NSError * ที่ชี้ไปจะต้องมีคุณสมบัติเป็นชี้ไปยังวัตถุที่ปล่อยอัตโนมัติ?
Proud Member

1
@Proud Member เกี่ยวกับความคิดเห็นแรกของคุณ - นั่นไม่ถูกต้อง (ถ้าฉันเข้าใจคุณถูกต้อง) - ดูคำตอบของ Glen Low ด้านล่าง ออบเจ็กต์ข้อผิดพลาดถูกสร้างและกำหนดให้กับตัวแปรการปล่อยอัตโนมัติ (อันที่คุณส่งผ่าน) ภายในฟังก์ชันบันทึก การมอบหมายนี้ทำให้ออบเจ็กต์ถูกเก็บรักษาและปล่อยอัตโนมัติในขณะนั้น การประกาศฟังก์ชันบันทึกป้องกันไม่ให้เราส่งสิ่งอื่นใดนอกจากตัวแปรการปล่อยอัตโนมัติเพราะนั่นคือสิ่งที่ต้องการซึ่งเป็นสาเหตุที่คอมไพลเลอร์สร้างตัวแปรชั่วคราวหากเราลอง
Colin

2
แล้วทำไมอินเทอร์เฟซของ Apple จึงดูเหมือนไม่มีสิ่งนี้? เช่นทุกอย่างใน NSFileManager.h?
พ่อ

1
@ Macmade: บังเอิญฉันสังเกตเห็นว่าคำตอบของคุณได้รับการแก้ไขแล้ว (โดยstackoverflow.com/users/12652/comptrol ) และฉันรู้สึกว่าอย่างน้อยการเปลี่ยนแปลงในตัวอย่างแรกของคุณ ("โดยปริยาย ... จะเปลี่ยนเป็น ... ) ผิดเพราะผู้คัดเลือก __strong ถูกย้ายจากบรรทัดที่สองไปยังบรรทัดแรกบางทีคุณอาจตรวจสอบได้
Martin R

34

ติดตามคำตอบของ Macmade และคำถามติดตามผลของ Proud Member ในความคิดเห็น (จะโพสต์สิ่งนี้เป็นความคิดเห็น แต่เกินจำนวนอักขระสูงสุด):

นี่คือสาเหตุที่ตัวแปรคุณสมบัติของ __autoreleasing ถูกวางไว้ระหว่างสองดาว

ในการนำหน้าไวยากรณ์ที่ถูกต้องสำหรับการประกาศตัวชี้วัตถุด้วยคุณสมบัติคือ:

NSError * __qualifier someError;

คอมไพเลอร์จะยกโทษให้สิ่งนี้:

__qualifier NSError *someError;

แต่มันไม่ถูกต้อง ดูคู่มือการเปลี่ยน Apple ARC (อ่านส่วนที่ขึ้นต้น "คุณควรตกแต่งตัวแปรให้ถูกต้อง ... ")

เพื่อตอบคำถามในมือ: ตัวชี้คู่ไม่สามารถมีคุณสมบัติการจัดการหน่วยความจำ ARC ได้เนื่องจากตัวชี้ที่ชี้ไปยังที่อยู่หน่วยความจำเป็นตัวชี้ไปยังประเภทดั้งเดิมไม่ใช่ตัวชี้ไปที่วัตถุ อย่างไรก็ตามเมื่อคุณประกาศตัวชี้คู่ ARC ต้องการทราบว่ากฎการจัดการหน่วยความจำสำหรับตัวชี้ที่สองคืออะไร นั่นเป็นเหตุผลที่ระบุตัวแปรตัวชี้คู่เป็น:

SomeClass * __qualifier *someVariable;

ดังนั้นในกรณีของอาร์กิวเมนต์เมธอดที่เป็นตัวชี้ NSError คู่ชนิดข้อมูลจะถูกประกาศเป็น:

- (BOOL)save:(NSError* __autoreleasing *)errorPointer;

ซึ่งในภาษาอังกฤษพูดว่า "pointer to an __autoreleasing NSError object pointer"


15

ข้อกำหนด ARC ชัดเจนกล่าวว่า

สำหรับ __autoreleasing วัตถุตัวชี้ใหม่จะถูกเก็บรักษาไว้ปล่อยอัตโนมัติและเก็บไว้ใน lvalue โดยใช้ความหมายดั้งเดิม

ตัวอย่างเช่นรหัส

NSError* __autoreleasing error = someError;

ได้รับการแปลงเป็นไฟล์

NSError* error = [[someError retain] autorelease];

... ซึ่งเป็นสาเหตุที่ใช้งานได้เมื่อคุณมีพารามิเตอร์NSError* __autoreleasing * errorPointerเมธอดที่เรียกว่าจะกำหนดข้อผิดพลาดให้*errorPointerและความหมายข้างต้นจะเริ่มทำงาน

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

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