Ya, ada manfaat menggunakan instancetype
dalam 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 id
sebagai nilai balik dengan yang instancetype
sesuai. Ini biasanya berlaku untuk init
metode dan metode pabrik kelas. Meskipun kompiler secara otomatis mengonversi metode yang dimulai dengan "alokasi," "init," atau "baru" dan memiliki tipe id
pengembalian untuk kembali instancetype
, itu tidak mengkonversi metode lain. Konvensi Objective-C adalah menulis instancetype
secara 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 id
ke instancetype
. Itu id
adalah objek generik. Tetapi jika Anda menjadikannya instancetype
kompiler 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 fileHandleWithStandardOutput
sebagai mengembalikan suatuinstancetype
. Maka para pemeran atau tugas tidak perlu.
(Perhatikan bahwa pada iOS, contoh ini tidak akan menghasilkan kesalahan karena hanya NSFileHandle
menyediakan kesalahan di writeData:
sana. Contoh lain ada, seperti length
, yang mengembalikan a CGFloat
dari UILayoutSupport
tetapi a NSUInteger
dari NSString
.)
Catatan : Sejak saya menulis ini, header macOS telah dimodifikasi untuk mengembalikan NSFileHandle
bukan 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 instancetype
dari init
. Tetapi ini karena kompiler secara otomatis mengonversi id
ke instancetype
. Anda mengandalkan kekhasan ini; saat Anda menulis bahwa init
mengembalikan 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 init
dan 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 init
fungsi dan juga pabrik kelas.
Jika Anda menggunakan id
untuk 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 instancetype
saat 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 instancetype
lebih sering.