Có, có những lợi ích khi sử dụng instancetypetrong mọi trường hợp áp dụng. Tôi sẽ giải thích chi tiết hơn, nhưng hãy để tôi bắt đầu với tuyên bố táo bạo này: Sử dụng instancetypebất cứ khi nào nó phù hợp, đó là bất cứ khi nào một lớp trả về một thể hiện của cùng một lớp.
Trên thực tế, đây là những gì Apple nói về chủ đề này:
Trong mã của bạn, thay thế các lần xuất hiện iddưới dạng giá trị trả về bằng instancetypekhi thích hợp. Đây thường là trường hợp cho initcác phương thức và phương thức nhà máy lớp. Mặc dù trình biên dịch tự động chuyển đổi các phương thức bắt đầu bằng phân bổ, phân phối, khởi động hoặc nâng cấp mới và có kiểu idtrả về instancetype, nhưng nó không chuyển đổi các phương thức khác. Quy ước Objective-C là viết instancetyperõ ràng cho tất cả các phương thức.
Dù vậy, chúng ta hãy tiếp tục và giải thích lý do tại sao đó là một ý tưởng tốt.
Đầu tiên, một số định nghĩa:
@interface Foo:NSObject
- (id)initWithBar:(NSInteger)bar; // initializer
+ (id)fooWithBar:(NSInteger)bar; // class factory
@end
Đối với một nhà máy đẳng cấp, bạn nên luôn luôn sử dụng instancetype. Trình biên dịch không tự động chuyển đổi idsang instancetype. Đó idlà một đối tượng chung chung. Nhưng nếu bạn làm cho nó một instancetypetrình biên dịch sẽ biết loại đối tượng mà phương thức trả về.
Đây không phải là một vấn đề học tập. Chẳng hạn, [[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData]sẽ tạo ra một lỗi trên Mac OS X ( chỉ ) Nhiều phương thức có tên 'writeData:' được tìm thấy với kết quả không khớp, loại tham số hoặc thuộc tính . Lý do là cả NSFileHandle và NSURLHandle đều cung cấp a writeData:. Vì [NSFileHandle fileHandleWithStandardOutput]trả về một id, trình biên dịch không chắc chắn lớp nào writeData:đang được gọi.
Bạn cần phải giải quyết vấn đề này bằng cách sử dụng:
[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];
hoặc là:
NSFileHandle *fileHandle = [NSFileHandle fileHandleWithStandardOutput];
[fileHandle writeData:formattedData];
Tất nhiên, giải pháp tốt hơn là tuyên bố fileHandleWithStandardOutputlà trả lại một instancetype. Sau đó, diễn viên hoặc nhiệm vụ không cần thiết.
(Lưu ý rằng trên iOS, ví dụ này sẽ không tạo ra lỗi vì chỉ NSFileHandlecung cấp writeData:ở đó. Các ví dụ khác tồn tại, chẳng hạn như length, trả về một CGFloattừ UILayoutSupportnhưng NSUIntegertừ một NSString.)
Lưu ý : Kể từ khi tôi viết bài này, các tiêu đề macOS đã được sửa đổi để trả về NSFileHandlethay vì một id.
Đối với người khởi tạo, nó phức tạp hơn. Khi bạn gõ này:
- (id)initWithBar:(NSInteger)bar
Thay vào đó, trình biên dịch sẽ giả vờ bạn gõ cái này thay vào đó:
- (instancetype)initWithBar:(NSInteger)bar
Điều này là cần thiết cho ARC. Điều này được mô tả trong Clang Language Extension Các loại kết quả liên quan . Đây là lý do tại sao mọi người sẽ nói với bạn rằng nó không cần thiết để sử dụng instancetype, mặc dù tôi cho rằng bạn nên. Phần còn lại của câu trả lời này liên quan đến điều này.
Có ba ưu điểm:
- Rõ ràng. Mã của bạn đang làm những gì nó nói, chứ không phải là một cái gì đó khác.
- Mẫu. Bạn đang xây dựng những thói quen tốt cho những lần nó không thành vấn đề.
- Tính nhất quán. Bạn đã thiết lập một số tính nhất quán cho mã của bạn, làm cho nó dễ đọc hơn.
Rõ ràng
Đúng là không có lợi ích kỹ thuật để trở về instancetypetừ một init. Nhưng điều này là do trình biên dịch tự động chuyển đổi idthành instancetype. Bạn đang dựa vào điều này; trong khi bạn đang viết rằng inittrả về một id, trình biên dịch sẽ diễn giải nó như thể nó trả về một instancetype.
Đây là tương đương với trình biên dịch:
- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;
Đây không phải là tương đương với đôi mắt của bạn. Tốt nhất, bạn sẽ học cách bỏ qua sự khác biệt và lướt qua nó. Đây không phải là điều bạn nên học cách bỏ qua.
Mẫu
Trong khi không có sự khác biệt với initcác hình thức khác, có là một sự khác biệt ngay sau khi bạn xác định một nhà máy lớp.
Hai cái này không tương đương:
+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Bạn muốn hình thức thứ hai. Nếu bạn quen gõ instancetypenhư kiểu trả về của hàm tạo, bạn sẽ hiểu đúng mỗi lần.
Tính nhất quán
Cuối cùng, hãy tưởng tượng nếu bạn kết hợp tất cả lại với nhau: bạn muốn một inithàm và cũng là một nhà máy lớp.
Nếu bạn sử dụng idcho init, bạn kết thúc với mã như thế này:
- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Nhưng nếu bạn sử dụng instancetype, bạn sẽ nhận được điều này:
- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Nó phù hợp hơn và dễ đọc hơn. Họ trả lại điều tương tự, và bây giờ điều đó là hiển nhiên.
Phần kết luận
Trừ khi bạn cố tình viết mã cho trình biên dịch cũ, bạn nên sử dụng instancetypekhi thích hợp.
Bạn nên do dự trước khi viết một tin nhắn trả về id. Hãy tự hỏi: Đây có phải là một ví dụ của lớp này? Nếu vậy, nó là một instancetype.
Chắc chắn có những trường hợp bạn cần quay lại id, nhưng có lẽ bạn sẽ sử dụng instancetypethường xuyên hơn nhiều.