Ya, ada manfaat menggunakan instancetypedalam semua kasus yang berlaku. Saya akan menjelaskan lebih detail, tetapi saya akan mulai dengan pernyataan tebal ini: Gunakaninstancetype kapan saja sesuai, yang mana setiap kali kelas mengembalikan instance dari kelas yang sama.
Faktanya, inilah yang dikatakan Apple tentang masalah ini:
Dalam kode Anda, ganti kejadian idsebagai nilai balik dengan yang instancetypesesuai. Ini biasanya berlaku untuk initmetode dan metode pabrik kelas. Meskipun kompiler secara otomatis mengonversi metode yang dimulai dengan "alokasi," "init," atau "baru" dan memiliki tipe idpengembalian untuk kembali instancetype, itu tidak mengkonversi metode lain. Konvensi Objective-C adalah menulis instancetypesecara eksplisit untuk semua metode.
Dengan itu, mari kita lanjutkan dan jelaskan mengapa itu ide yang bagus.
Pertama, beberapa definisi:
@interface Foo:NSObject
- (id)initWithBar:(NSInteger)bar; // initializer
+ (id)fooWithBar:(NSInteger)bar; // class factory
@end
Untuk pabrik kelas, Anda harus selalu menggunakannya instancetype. Kompiler tidak secara otomatis dikonversi idke instancetype. Itu idadalah objek generik. Tetapi jika Anda menjadikannya instancetypekompiler tahu jenis objek apa metode kembali.
Ini bukan masalah akademis. Misalnya, [[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData]akan menghasilkan kesalahan pada Mac OS X ( hanya ) Beberapa metode bernama 'writeData:' ditemukan dengan hasil yang tidak cocok, tipe parameter atau atribut . Alasannya adalah bahwa NSFileHandle dan NSURLHandle menyediakan a writeData:. Sejak [NSFileHandle fileHandleWithStandardOutput]mengembalikan sebuah id, kompiler tidak yakin kelas apa writeData:yang dipanggil.
Anda perlu mengatasinya, menggunakan:
[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];
atau:
NSFileHandle *fileHandle = [NSFileHandle fileHandleWithStandardOutput];
[fileHandle writeData:formattedData];
Tentu saja, solusi yang lebih baik adalah mendeklarasikan fileHandleWithStandardOutputsebagai mengembalikan suatuinstancetype . Maka para pemeran atau tugas tidak perlu.
(Perhatikan bahwa pada iOS, contoh ini tidak akan menghasilkan kesalahan karena hanya NSFileHandlemenyediakan kesalahan di writeData:sana. Contoh lain ada, seperti length, yang mengembalikan a CGFloatdari UILayoutSupporttetapi a NSUIntegerdari NSString.)
Catatan : Sejak saya menulis ini, header macOS telah dimodifikasi untuk mengembalikan NSFileHandlebukan id.
Untuk inisialisasi, ini lebih rumit. Saat Anda mengetik ini:
- (id)initWithBar:(NSInteger)bar
... kompiler akan berpura-pura Anda mengetik ini sebagai gantinya:
- (instancetype)initWithBar:(NSInteger)bar
Ini diperlukan untuk ARC. Ini dijelaskan dalam tipe hasil Terkait Ekstensi Bahasa Dentang . Inilah sebabnya mengapa orang akan mengatakan kepada Anda bahwa itu tidak perlu digunakaninstancetype , meskipun saya berpendapat Anda harus menggunakannya. Sisa dari jawaban ini berkaitan dengan ini.
Ada tiga keuntungan:
- Eksplisit. Kode Anda melakukan apa yang dikatakannya, bukan sesuatu yang lain.
- Pola. Anda sedang membangun kebiasaan yang baik untuk saat-saat yang penting, yang memang ada.
- Konsistensi. Anda telah membuat beberapa konsistensi terhadap kode Anda, yang membuatnya lebih mudah dibaca.
Eksplisit
Memang benar bahwa tidak ada manfaat teknis untuk kembali instancetypedari init. Tetapi ini karena kompiler secara otomatis mengonversi idke instancetype. Anda mengandalkan kekhasan ini; saat Anda menulis bahwa initmengembalikan sebuah id, kompiler menafsirkannya seolah-olah mengembalikan sebuahinstancetype .
Ini sama dengan kompiler:
- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;
Ini tidak setara dengan mata Anda. Paling-paling, Anda akan belajar untuk mengabaikan perbedaan dan membaca sepintas lalu. Ini bukan sesuatu yang harus Anda pelajari untuk diabaikan.
Pola
Meskipun tidak ada perbedaan dengan initdan metode lain, ada adalah perbedaan segera setelah Anda mendefinisikan sebuah pabrik kelas.
Keduanya tidak setara:
+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Anda menginginkan bentuk kedua. Jika Anda terbiasa mengetikinstancetype sebagai tipe pengembalian konstruktor, Anda akan melakukannya dengan benar setiap waktu.
Konsistensi
Akhirnya, bayangkan jika Anda menggabungkan semuanya: Anda menginginkan initfungsi dan juga pabrik kelas.
Jika Anda menggunakan iduntuk init, Anda berakhir dengan kode seperti ini:
- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Tetapi jika Anda menggunakan instancetype, Anda mendapatkan ini:
- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Ini lebih konsisten dan lebih mudah dibaca. Mereka mengembalikan hal yang sama, dan sekarang sudah jelas.
Kesimpulan
Kecuali Anda sengaja menulis kode untuk kompiler lama, Anda harus menggunakannya instancetypesaat yang tepat.
Anda harus ragu sebelum menulis pesan yang mengembalikan id. Tanyakan kepada diri sendiri: Apakah ini mengembalikan instance kelas ini? Jika demikian, ini adalah instancetype.
Tentu saja ada kasus di mana Anda harus kembali id, tetapi Anda mungkin akan menggunakan instancetypelebih sering.