ใช่มีประโยชน์ที่จะใช้instancetype
ในทุกกรณีที่ใช้ ฉันจะอธิบายรายละเอียดเพิ่มเติม แต่ให้ฉันเริ่มต้นด้วยคำสั่งตัวหนานี้: ใช้instancetype
เมื่อใดก็ตามที่เหมาะสมซึ่งเมื่อใดก็ตามที่คลาสส่งคืนอินสแตนซ์ของคลาสเดียวกันนั้น
ในความเป็นจริงนี่คือสิ่งที่ Apple พูดในเรื่องนี้:
ในรหัสของคุณให้แทนที่ค่าที่เกิดขึ้นid
ซึ่งเป็นค่าตอบแทนinstancetype
ที่เหมาะสม โดยทั่วไปจะเป็นกรณีสำหรับinit
วิธีการและวิธีการเรียนโรงงาน แม้ว่าคอมไพเลอร์จะแปลงวิธีการที่ขึ้นต้นด้วย“ alloc,”“ init” หรือ“ ใหม่” โดยอัตโนมัติและมีประเภทid
การส่งคืนเพื่อส่งคืนinstancetype
แต่จะไม่แปลงวิธีอื่น ๆ การประชุมเชิงวัตถุประสงค์ -C คือการเขียนinstancetype
อย่างชัดเจนสำหรับวิธีการทั้งหมด
ลองทำต่อไปและอธิบายว่าทำไมจึงเป็นความคิดที่ดี
ก่อนคำจำกัดความบางอย่าง:
@interface Foo:NSObject
- (id)initWithBar:(NSInteger)bar; // initializer
+ (id)fooWithBar:(NSInteger)bar; // class factory
@end
สำหรับโรงงานคลาสคุณควรเสมอinstancetype
ใช้ คอมไพเลอร์ไม่ได้โดยอัตโนมัติแปลงไปid
instancetype
นั่นid
เป็นวัตถุทั่วไป แต่ถ้าคุณทำให้instancetype
คอมไพเลอร์รู้ว่าประเภทของวัตถุวิธีการส่งกลับ
นี่ไม่ใช่ปัญหาทางวิชาการ ยกตัวอย่างเช่น[[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData]
จะสร้างข้อผิดพลาดใน Mac OS X ( เท่านั้น ) วิธีการหลายชื่อ 'writeData:' พบกับผลลัพธ์ที่ไม่ตรงกันชนิดพารามิเตอร์หรือแอตทริบิวต์ เหตุผลก็คือว่าทั้งสอง NSFileHandle และ NSURLHandle writeData:
ให้ ตั้งแต่[NSFileHandle fileHandleWithStandardOutput]
ส่งคืนid
คอมไพเลอร์ไม่แน่ใจว่าคลาสwriteData:
ใดที่ถูกเรียกใช้
คุณต้องหลีกเลี่ยงปัญหานี้โดยใช้:
[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];
หรือ:
NSFileHandle *fileHandle = [NSFileHandle fileHandleWithStandardOutput];
[fileHandle writeData:formattedData];
แน่นอนว่าทางออกที่ดีกว่าคือการประกาศเป็นส่งคืนfileHandleWithStandardOutput
instancetype
จากนั้นนักแสดงหรือการมอบหมายก็ไม่จำเป็น
(โปรดสังเกตว่าบน iOS เช่นนี้จะไม่สร้างข้อผิดพลาดเป็นเพียงNSFileHandle
ให้writeData:
มี. ตัวอย่างอื่น ๆ ที่มีอยู่เช่นlength
ที่ส่งกลับCGFloat
จากUILayoutSupport
แต่NSUInteger
จากNSString
.)
หมายเหตุ : เนื่องจากผมเขียนนี้ส่วนหัว MacOS ได้รับการแก้ไขให้กลับมาเป็นแทนNSFileHandle
id
สำหรับ initializers มันซับซ้อนกว่า เมื่อคุณพิมพ์สิ่งนี้:
- (id)initWithBar:(NSInteger)bar
…ผู้เรียบเรียงจะแกล้งคุณพิมพ์สิ่งนี้แทน:
- (instancetype)initWithBar:(NSInteger)bar
นี่เป็นสิ่งจำเป็นสำหรับ ARC นี้จะอธิบายในภาษาเสียงดังกราวส่วนขยายประเภทผลที่เกี่ยวข้อง นี่คือเหตุผลที่ผู้คนจะบอกคุณว่าไม่จำเป็นต้องใช้instancetype
แม้ว่าฉันจะโต้แย้งคุณควร ส่วนที่เหลือของคำตอบนี้เกี่ยวข้องกับเรื่องนี้
มีสามข้อได้เปรียบ:
- ชัดเจน. รหัสของคุณกำลังทำสิ่งที่มันพูดแทนที่จะเป็นอย่างอื่น
- แบบแผน คุณกำลังสร้างนิสัยที่ดีสำหรับเวลาที่สำคัญ
- ความมั่นคง คุณได้สร้างความมั่นคงให้กับโค้ดของคุณซึ่งทำให้อ่านง่ายขึ้น
ชัดเจน
มันเป็นความจริงว่าไม่มีทางเทคนิคประโยชน์ที่จะกลับมาจากinstancetype
init
แต่นี้เป็นเพราะคอมไพเลอร์โดยอัตโนมัติแปลงไปid
instancetype
คุณพึ่งพาการเล่นโวหารนี้; ในขณะที่คุณกำลังเขียนว่าinit
ส่งกลับคอมไพเลอร์จะถูกแปลความหมายของมันราวกับว่ามันส่งกลับid
instancetype
สิ่งเหล่านี้เทียบเท่ากับคอมไพเลอร์:
- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;
สิ่งเหล่านี้ไม่เทียบเท่ากับดวงตาของคุณ ที่ดีที่สุดคุณจะได้เรียนรู้ที่จะไม่สนใจความแตกต่างและอ่านผ่าน ๆ นี่ไม่ใช่สิ่งที่คุณควรเรียนรู้ที่จะเพิกเฉย
แบบแผน
ในขณะที่มีความแตกต่างใด ๆ กับinit
และวิธีการอื่น ๆ ที่มีคือความแตกต่างทันทีที่คุณกำหนดโรงงานชั้นเรียน
ทั้งสองนี้ไม่เทียบเท่า:
+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
คุณต้องการรูปแบบที่สอง หากคุณคุ้นเคยกับการพิมพ์instancetype
เป็นชนิดส่งคืนของตัวสร้างคุณจะทำให้ถูกต้องทุกครั้ง
ความมั่นคง
ในที่สุดลองนึกภาพถ้าคุณรวมมันเข้าด้วยกัน: คุณต้องการinit
ฟังก์ชั่นและโรงงานคลาส
ถ้าคุณใช้id
สำหรับinit
คุณท้ายด้วยรหัสเช่นนี้
- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
แต่ถ้าคุณใช้instancetype
คุณจะได้รับสิ่งนี้:
- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
มันสอดคล้องและอ่านได้มากขึ้น พวกเขากลับมาเหมือนเดิมและตอนนี้เห็นได้ชัด
ข้อสรุป
หากคุณไม่ได้ตั้งใจเขียนโค้ดสำหรับคอมไพเลอร์เก่าคุณควรใช้instancetype
ตามความเหมาะสม
id
คุณควรลังเลก่อนที่จะเขียนข้อความที่ส่งกลับ ถามตัวเองว่า: นี่เป็นตัวอย่างของคลาสนี้หรือไม่? ถ้าเป็นinstancetype
เช่นนั้น
มีหลายกรณีที่คุณต้องส่งคืนid
แต่คุณอาจใช้instancetype
บ่อยกว่านี้