ฉันตั้งค่าคุณสมบัติเป็นศูนย์ใน dealloc เมื่อใช้ ARC หรือไม่


125

ฉันกำลังพยายามเรียนรู้การนับอ้างอิงอัตโนมัติใน iOS 5 ตอนนี้ส่วนแรกของคำถามนี้น่าจะง่าย:

  1. ถูกต้องหรือไม่ที่ฉันไม่จำเป็นต้องเขียนคำสั่ง release-property อย่างชัดเจนใน dealloc ของฉันเมื่อใช้ ARC กล่าวอีกนัยหนึ่งเป็นความจริงหรือไม่ว่าสิ่งต่อไปนี้ไม่จำเป็นต้องมี dealloc ที่ชัดเจน?

    @interface MyClass : NSObject
    @property (strong, nonatomic) NSObject* myProperty;
    @end
    
    @implementation MyClass
    @synthesize myProperty;
    @end
  2. คำถามถัดไปและที่สำคัญกว่าของฉันมาจากบรรทัดในเอกสารการเปลี่ยนไปใช้ ARC Release Notes :

    คุณไม่จำเป็นต้องปล่อยตัวแปรอินสแตนซ์ แต่คุณอาจต้องเรียกใช้ [self setDelegate: nil] ในคลาสระบบและโค้ดอื่น ๆ ที่ไม่ได้คอมไพล์โดยใช้ ARC

    สิ่งนี้ทำให้เกิดคำถาม: ฉันจะรู้ได้อย่างไรว่าคลาสระบบใดที่ไม่ได้คอมไพล์กับ ARC เมื่อใดที่ฉันควรสร้าง dealloc ของตัวเองและตั้งค่าอย่างชัดเจนเพื่อรักษาคุณสมบัติให้เป็นศูนย์ ฉันควรถือว่าคลาสเฟรมเวิร์ก NS และ UI ทั้งหมดที่ใช้ในคุณสมบัติจำเป็นต้องใช้เอกสารที่ชัดเจนหรือไม่

มีข้อมูลมากมายเกี่ยวกับ SO และที่อื่น ๆ เกี่ยวกับแนวทางปฏิบัติในการปล่อย ivar สำรองของคุณสมบัติเมื่อใช้การติดตามอ้างอิงด้วยตนเอง แต่ค่อนข้างน้อยเกี่ยวกับเรื่องนี้เมื่อใช้ ARC

คำตอบ:


197

คำตอบสั้น ๆ : ไม่คุณไม่จำเป็นต้องปิดคุณสมบัติในdeallocARC

คำตอบแบบยาว : คุณไม่ควรปิดคุณสมบัติdeallocแม้แต่ในการจัดการหน่วยความจำด้วยตนเอง

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

ใน ARC ระบบอัตโนมัติออก ivars ใด ๆ deallocสำหรับคุณดังนั้นถ้านั่นคือทั้งหมดที่คุณทำคุณไม่ได้มีการดำเนินการ แต่ถ้าคุณมี ivars ไม่ใช่วัตถุใด ๆ ที่จำเป็นต้องมีการจัดการพิเศษ (เช่นบัฟเฟอร์จัดสรรที่คุณจำเป็นต้องfree()) deallocคุณยังคงมีการจัดการกับผู้ที่อยู่ใน

นอกจากนี้หากคุณตั้งตัวเองเป็นผู้รับมอบสิทธิ์ของวัตถุใด ๆ คุณควรยกเลิกการตั้งค่าความสัมพันธ์นั้นในdealloc(นี่คือบิตเกี่ยวกับการโทร[obj setDelegate:nil]) หมายเหตุเกี่ยวกับการทำสิ่งนี้ในคลาสที่ไม่ได้คอมไพล์ด้วย ARC เป็นการพยักหน้าต่อคุณสมบัติที่อ่อนแอ หากคลาสนั้นทำเครื่องหมายdelegateคุณสมบัติของมันอย่างชัดเจนweakคุณก็ไม่จำเป็นต้องทำเช่นนี้เนื่องจากลักษณะของคุณสมบัติที่อ่อนแอหมายความว่ามันจะถูกลบล้างให้คุณ อย่างไรก็ตามหากมีการทำเครื่องหมายคุณสมบัติassignคุณควรปิดมันไว้ในของคุณdeallocมิฉะนั้นชั้นเรียนจะเหลือเพียงตัวชี้ห้อยและอาจจะผิดพลาดหากพยายามส่งข้อความถึงผู้รับมอบสิทธิ์ โปรดทราบว่าสิ่งนี้ใช้เฉพาะกับความสัมพันธ์ที่ไม่ได้เก็บรักษาไว้เช่นผู้รับมอบสิทธิ์


2
นี่เข้าท่า! ให้ฉันถามคุณว่า: สถานการณ์ทั่วไปที่ฉันมีคือฉันมีMyController : UIViewControllerคลาสที่สร้างและเป็นเจ้าของ UIView และยังตั้งค่าผู้แทนของมุมมองให้กับตัวเอง เป็นเจ้าของข้อมูลพร็อพเพอร์ตี้ดังกล่าว แต่เพียงผู้เดียว เมื่อคอนโทรลเลอร์ได้รับการยกเลิกการจัดสรรมุมมองก็ควรได้รับการลดการปันส่วน แล้วตัวชี้ผู้รับมอบสิทธิ์จะห้อยหรือไม่?
emfurry

4
@emfurry: อาจจะไม่เป็นเช่นนั้นเพราะเมื่อถึงเวลาที่ตัวควบคุมมุมมองของคุณตายมุมมองนั้นไม่ควรอยู่ในลำดับชั้นของมุมมองและไม่ควรทำอะไรเลย แต่เป็นการดีที่สุดที่จะไม่ตั้งสมมติฐาน จะเกิดอะไรขึ้นถ้ามุมมองงานที่กำหนดเวลาไว้แบบอะซิงโครนัสต้องทำในภายหลังและมุมมองนั้นจบลงด้วยการใช้งานตัวควบคุมมุมมองในช่วงเวลาสั้น ๆ (เช่นเนื่องจากงานแบบอะซิงโครนัสที่รักษามุมมองไว้ชั่วคราว) เป็นการดีที่สุดที่จะลบล้างผู้รับมอบสิทธิ์ให้ปลอดภัย และในความเป็นจริงหากมุมมองที่เป็นปัญหาคือ a UIWebViewเอกสารจะระบุอย่างชัดเจนว่าคุณจำเป็นต้องปิดผู้รับมอบสิทธิ์
Lily Ballard

3
@zeiteisen: ไม่unsafe_unretainedเทียบเท่ากับassignคุณสมบัติและเป็นพฤติกรรมปกติสำหรับความสัมพันธ์ของผู้ร่วมประชุมภายใต้ MRR และสิ่งเหล่านี้จำเป็นต้องถูกลบออก
Lily Ballard

4
ฉันไม่เห็นด้วยกับคำชี้แจงเกี่ยวกับการไม่ใช้ setters ใน dealloc กับ MRC Apple ไม่แนะนำ แต่ทำในรหัสด้วย คุณสามารถสร้างปัญหาใหม่ได้โดยไม่ใช้ setter มีการอภิปรายใหญ่ ๆ เกี่ยวกับเรื่องนี้ สิ่งที่ไม่เข้าใจคือการเขียนตัวตั้งค่าอย่างถูกต้อง (ต้องทำงานอย่างถูกต้องหากคุณส่งผ่านค่าศูนย์) และบางครั้งก็ดูลำดับการยกเลิกการจัดสรร
Sulthan

7
@Sulthan: ไม่ว่าจะใช้ setters ใน dealloc เป็นเวิร์มขนาดใหญ่ได้หรือไม่ แต่โดยพื้นฐานแล้วตำแหน่งของฉันลดลงไปที่: คุณต้องการเรียกรหัสให้น้อยที่สุดใน dealloc Setters มีแนวโน้มที่จะรวมผลข้างเคียงไม่ว่าจะโดยการแทนที่ในคลาสย่อยหรือผ่าน KVO หรือกลไกอื่น ๆ โดยเฉพาะอย่างยิ่งควรหลีกเลี่ยงผลข้างเคียงใน dealloc เช่นโรคระบาด หากคุณสามารถลบ method call ออกจาก dealloc ได้คุณควรทำเช่นนั้น สิ่งนี้ทำให้ง่ายขึ้นเป็น: อย่าเรียก setters ใน dealloc
Lily Ballard

2

เพียงเพื่อให้คำตอบตรงกันข้าม ...

คำตอบสั้น ๆ : ไม่คุณไม่จำเป็นต้องปิดคุณสมบัติที่สังเคราะห์อัตโนมัติในdeallocภายใต้ ARC initและคุณไม่ต้องใช้ตัวตั้งค่าสำหรับผู้ที่อยู่ใน

คำตอบแบบยาว : คุณควรปิดคุณสมบัติที่สังเคราะห์ขึ้นเองในdeallocแม้ภายใต้ ARC และคุณควรใช้ตัวตั้งค่าสำหรับผู้ที่เข้าinitมา

ประเด็นคือคุณสมบัติที่สังเคราะห์ขึ้นเองของคุณควรปลอดภัยและสมมาตรเกี่ยวกับการทำให้เป็นโมฆะ

ตัวตั้งเวลาที่เป็นไปได้สำหรับตัวจับเวลา:

-(void)setTimer:(NSTimer *)timer
{
    if (timer == _timer)
        return;

    [timer retain];
    [_timer invalidate];
    [_timer release];
    _timer = timer;
    [_timer fire];
}

ตัวตั้งค่าที่เป็นไปได้สำหรับ scrollview, tableview, webview, textfield, ... :

-(void)setScrollView:(UIScrollView *)scrollView
{
    if (scrollView == _scrollView)
        return;

    [scrollView retain];
    [_scrollView setDelegate:nil];
    [_scrollView release];
    _scrollView = scrollView;
    [_scrollView setDelegate:self];
}

ตัวตั้งค่าที่เป็นไปได้สำหรับคุณสมบัติ KVO:

-(void)setButton:(UIButton *)button
{
    if (button == _button)
        return;

    [button retain];
    [_button removeObserver:self forKeyPath:@"tintColor"];
    [_button release];
    _button = button;
    [_button addObserver:self forKeyPath:@"tintColor" options:(NSKeyValueObservingOptions)0 context:NULL];
}

แล้วคุณจะได้ไม่ต้องซ้ำรหัสใด ๆ สำหรับdealloc, didReceiveMemoryWarning, viewDidUnload... และทรัพย์สินของคุณได้อย่างปลอดภัยจะทำให้ประชาชน หากคุณกังวลเกี่ยวกับคุณสมบัติที่ไม่มีอยู่deallocอาจถึงเวลาที่คุณต้องตรวจสอบตัวตั้งค่าของคุณอีกครั้ง

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