ทำไมผู้แทน Objective-C ถึงได้รับมอบหมายคุณสมบัติแทนการเก็บรักษา?


176

ฉันท่องผ่านบล็อกที่ยอดเยี่ยมซึ่งดูแลโดย Scott Stevenson และฉันพยายามเข้าใจแนวคิดพื้นฐานของ Objective-C ในการมอบหมายผู้มอบหมาย 'มอบหมาย' คุณสมบัติ 'เทียบกับ' รักษา ' หมายเหตุทั้งคู่เหมือนกันในสภาพแวดล้อมที่เก็บขยะ ฉันส่วนใหญ่เกี่ยวข้องกับสภาพแวดล้อมที่ไม่ใช่ GC (เช่น: iPhone)

โดยตรงจากบล็อกของสกอตต์:

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

หมายความว่าคุณไม่ได้เป็นเจ้าของวัตถุผู้รับมอบสิทธิ์โดยตรง ฉันมักจะรักษาผู้ได้รับมอบหมายของฉันเพราะถ้าฉันไม่ต้องการให้พวกเขาออกไปสู่ห้วงลึก ฉันมักจะนามธรรม UITableViewController ห่างจากแหล่งข้อมูลที่เกี่ยวข้องและผู้ได้รับมอบหมายด้วย ฉันยังคงวัตถุเฉพาะนั้นไว้ ฉันต้องการให้แน่ใจว่ามันจะไม่หายไปดังนั้น UITableView ของฉันจะมีตัวแทนอยู่รอบ ๆ เสมอ

บางคนสามารถอธิบายเพิ่มเติมว่าที่ไหน / ทำไมฉันจึงผิดฉันจึงสามารถเข้าใจกระบวนทัศน์ทั่วไปนี้ในการเขียนโปรแกรม Objective-C 2.0 ของการใช้คุณสมบัติมอบหมายให้กับผู้ได้รับมอบหมายแทนที่จะเก็บไว้?

ขอบคุณ!


ติดแท็กด้วย "ผู้รับมอบสิทธิ์" และไม่มี "iphone"
Quinn Taylor

เหตุใดตัวแทนจึงมอบหมายให้แทนการทำสำเนา (เช่น NSString?)
OMGPOP

คำตอบ:


175

เหตุผลที่คุณหลีกเลี่ยงการมอบหมายผู้ร่วมประชุมคือคุณต้องหลีกเลี่ยงรอบการเก็บ:

A สร้าง B A ตั้งตัวเองเป็นตัวแทนของ B … A ถูกปล่อยโดยเจ้าของ

ถ้า B เก็บ A ไว้จะไม่ปล่อย A ในฐานะเจ้าของ B ดังนั้น A ของ dealloc จะไม่ถูกเรียกใช้ทำให้ทั้ง A และ Bรั่วไหล

คุณไม่ควรกังวลเกี่ยวกับการจากไปเพราะเป็นเจ้าของ B และกำจัดมันใน dealloc


ฉันไม่เห็นด้วยไมค์ ฉันเพิ่งพบปัญหาที่โมดัลมีผู้รับมอบสิทธิ์ที่ยกเลิกคำกริยา แต่เมื่อฉันเตือนหน่วยความจำในคำกริยามันจะเผยแพร่ผู้รับมอบสิทธิ์ จากนั้นเมื่อฉันไปยกเลิกคำกริยาของฉันผู้รับมอบสิทธิ์เป็นศูนย์ ชน
Paul Shapiro

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

2
ไม่ควรเขียนรหัสของคุณในลักษณะที่ผู้มอบหมายไม่มีสาเหตุทำให้เกิดความผิดพลาด เฉพาะวัตถุที่เป็นเจ้าของควรมีการอ้างอิงการเป็นเจ้าของ เมื่อ dealloc'd มันจะต้องตั้งค่าผู้รับมอบสิทธิ์ของวัตถุที่เป็นเจ้าของก่อนที่จะปล่อยพวกเขา จากนั้นข้อความใด ๆ ที่ส่งถึงผู้รับมอบสิทธิ์ศูนย์จะถูกละเว้นเพียงแค่ อย่างไรก็ตามการส่งผ่านวัตถุใด ๆในข้อความอาจทำให้เกิดปัญหาได้ เพียงให้แน่ใจว่าคุณไม่ได้จัดการกับผู้ได้รับมอบหมายเช่นนั้น
David Gish

รอ - นั่นมันไม่อะไรweakเหรอ? คำถามคือทำไมใช้assignแทนweak?
wcochran

3
@wcochran: ไม่มีคำถามนี้คือเหตุผลที่ใช้แทนassign retainคำถามนั้นเก่ากว่า ARC; weakและstrong(ไม่มีความหมายเหมือนกันกับretain) ไม่มีอยู่จนกว่า ARC จะถูกนำมาใช้ คุณควรถามคำถามเกี่ยวweakกับ vs. assignแยกกัน
Peter Hosey

44

เนื่องจากวัตถุที่ส่งข้อความผู้รับมอบสิทธิ์ไม่ได้เป็นเจ้าของผู้รับมอบสิทธิ์

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

ในบางครั้งวัตถุนั้นเป็นเพื่อนร่วมงาน: ไม่มีใครเป็นเจ้าของวัตถุอื่นอาจเป็นเพราะวัตถุทั้งสามนั้นเป็นของทั้งคู่

ไม่ว่าด้วยวิธีใดวัตถุที่มีผู้รับมอบสิทธิ์ไม่ควรรักษาผู้รับมอบสิทธิ์ไว้

(มีอย่างน้อยหนึ่งข้อยกเว้นโดยวิธีฉันไม่จำว่ามันคืออะไรและฉันไม่คิดว่ามีเหตุผลที่ดีสำหรับมัน)


ภาคผนวก (เพิ่ม 2012-05-19): ภายใต้ ARC คุณควรใช้แทนweak assignการอ้างอิงที่อ่อนแอได้รับการตั้งค่าnilโดยอัตโนมัติเมื่อวัตถุตายเพื่อขจัดความเป็นไปได้ที่วัตถุการมอบหมายจะสิ้นสุดการส่งข้อความไปยังผู้รับมอบสิทธิ์ที่ตาย

หากคุณอยู่ห่างจาก ARC ด้วยเหตุผลบางอย่างอย่างน้อยก็เปลี่ยนassignคุณสมบัติที่ชี้ไปที่วัตถุunsafe_unretainedซึ่งทำให้ชัดเจนว่านี่เป็นการอ้างอิงที่ไม่ได้รับ แต่ไม่มีการอ้างอิงถึงวัตถุ

assign ยังคงเหมาะสมสำหรับค่าที่ไม่ใช่วัตถุภายใต้ทั้ง ARC และ MRC


13
NSURLConnectionรักษาผู้แทนของมัน

ใช่การใช้งานweakแต่ไม่ได้ตอบคำถามเดิม: ทำไมไม่ใช้แอปเปิ้ลassignแทนweak?
wcochran

@wcochran: คำถามเดิมว่า“ทำไมเป็นทรัพย์สินของผู้ร่วมประชุมได้รับassignมากกว่าการรักษา ”; weakไม่มีอยู่เมื่อถูกถาม คำถามของคุณแตกต่างกันซึ่งคุณควรถามแยกต่างหาก ฉันยินดีที่จะตอบ
Peter Hosey

@ wcochran และ Peter คำถามนั้นถูกถามที่อื่นไหม
Mr Rogers

17

โปรดทราบว่าเมื่อคุณมีผู้รับมอบสิทธิ์ที่ได้รับมอบหมายมันสำคัญมากที่จะต้องตั้งค่าผู้รับมอบสิทธิ์ให้เป็นศูนย์เสมอเมื่อใดก็ตามที่วัตถุจะถูกจัดสรรคืน - ดังนั้นวัตถุควรระมัดระวังไม่ให้มีการอ้างอิงตัวแทนใน dealloc หากไม่มี ทำที่อื่น


“ โปรดทราบว่าเมื่อคุณมีผู้รับมอบสิทธิ์ที่ได้รับมอบหมายมันสำคัญมากที่จะต้องตั้งค่าผู้รับมอบสิทธิ์ให้เป็นศูนย์เสมอเมื่อวัตถุถูกยกเลิกการจัดสรร” ทำไม?
ปีเตอร์ Hosey

2
เนื่องจากการตั้งค่าการอ้างอิงด้านซ้ายจะไม่ถูกต้องหลังจากวัตถุถูกจัดสรรคืน (ชี้ไปที่หน่วยความจำไม่ได้ถูกจัดสรรให้กับวัตถุชนิดที่คาดหวังไว้) - และทำให้เกิดความผิดพลาดหากคุณพยายามใช้ สัญญาณของสิ่งนี้ในตัวดีบั๊กคือเมื่อตัวดีบั๊กอ้างว่าตัวแปรบางตัวมีประเภทที่ดูเหมือนว่าผิดทั้งหมดจากสิ่งที่ตัวแปรนั้นถูกประกาศว่าเป็นจริง
Kendall Helmstetter Gelner

1
สิ่งนี้จำเป็นเฉพาะเมื่อวัตถุที่คุณเป็นผู้รับมอบสิทธิ์ถูกเก็บรักษาไว้โดยแหล่งอื่นเช่นตัวจับเวลาหรือการโทรกลับแบบอะซิงโครนัสอื่น ๆ มิฉะนั้นจะถูกยกเลิกการจัดสรรหลังจากที่คุณวางจำหน่ายและจะไม่พยายามเรียกวิธีการมอบสิทธิ์
Andrew Pouliot

@Andrew: มันเป็นเรื่องจริง แต่ถ้าคุณทำให้มันเป็นแบบฝึกหัดเพื่อกำจัดผู้ได้รับมอบหมายคุณจะไม่ลืมเมื่อมันเป็นเรื่องสำคัญหรือถ้าคุณตั้งใจที่จะเก็บวัตถุที่ถูกยึดไว้โดยไม่ตั้งใจ หากคุณไม่มีผู้ร่วมประชุมผลที่ได้จะเป็นเพียงแค่การรั่วไหลแทนที่จะเป็นการรั่วตามด้วยการชน
Kendall Helmstetter Gelner

1

หนึ่งในเหตุผลที่อยู่เบื้องหลังคือเพื่อหลีกเลี่ยงการเก็บรอบ เพียงเพื่อหลีกเลี่ยงสถานการณ์ที่ A และ B ทั้งสองออบเจ็กต์อ้างอิงกันและไม่มีสิ่งใดถูกปล่อยออกมาจากหน่วยความจำ

การกำหนดแบบอัตโนมัติเหมาะที่สุดสำหรับประเภทดั้งเดิมเช่น NSInteger และ CGFloat หรือวัตถุที่คุณไม่ได้เป็นเจ้าของโดยตรงเช่นผู้ได้รับมอบหมาย


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