Evet, instancetypegeçerli olduğu her durumda kullanmanın faydaları vardır . Daha ayrıntılı olarak açıklayacağım, ancak şu cesur ifadeyle başlayayım: Uygun instancetypeolduğunda kullanın , yani bir sınıf aynı sınıfın bir örneğini döndürdüğünde.
Aslında, Apple şu anda bu konuda şunları söylüyor:
Kodunuzda, iddönüş değeri olarak oluşumlarını instancetypeuygun olanlarla değiştirin. Bu genellikle inityöntemler ve sınıf fabrikası yöntemleri için geçerlidir. Derleyici otomatik olarak “alloc” “init” veya “yeni” ile başlayan ve bir dönüş türü yöntemleri dönüştürür olsa idgeri dönüşü instancetype, diğer metotların dönüştürmez. Amaç-C kuralı instancetypetüm yöntemler için açıkça yazmaktır .
Yoldan çekilip, bunun neden iyi bir fikir olduğunu açıklayalım.
İlk olarak, bazı tanımlar:
@interface Foo:NSObject
- (id)initWithBar:(NSInteger)bar; // initializer
+ (id)fooWithBar:(NSInteger)bar; // class factory
@end
Sınıf fabrikası için her zaman kullanmalısınız instancetype. Derleyici otomatik olarak dönüştürmez idiçin instancetype. Bu idgenel bir nesne. Ancak bunu bir instancetypederleyici yaparsanız , yöntemin ne tür bir nesne döndürdüğünü bilir.
Bu akademik bir sorun değil . Örneğin [[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData], Mac OS X'te bir hata oluşturur ( yalnızca ) Eşleşmeyen sonuç, parametre türü veya niteliklerle bulunan 'writeData:' adlı birden çok yöntem bulunur . Bunun nedeni hem NSFileHandle hem de NSURLHandle'ın a writeData:. Yana [NSFileHandle fileHandleWithStandardOutput]getiriler bir id, derleyici hangi sınıf kesin değildir writeData:çağrıldığı.
Aşağıdakilerden birini kullanarak bu soruna geçici bir çözüm bulmanız gerekir:
[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];
veya:
NSFileHandle *fileHandle = [NSFileHandle fileHandleWithStandardOutput];
[fileHandle writeData:formattedData];
Tabii ki, daha iyi bir çözüm fileHandleWithStandardOutputgeri dönen olarak ilan etmektir instancetype. Sonra oyuncu kadrosu veya ödev gerekli değildir.
(Yalnızca iOS'taki, bu örnek, bir hata üretmek olmayacak Not NSFileHandlebir sağlar writeData:vardır. Diğer örnekler, örneğin, mevcut lengthbir döndüren CGFloatgelen UILayoutSupportancak bir NSUIntegermesafede NSString).
Not : Bunu yazdığım için, macOS başlıkları bir NSFileHandleyerine döndürülecek şekilde değiştirildi id.
Başlatıcılar için daha karmaşıktır. Bunu yazdığınızda:
- (id)initWithBar:(NSInteger)bar
… Derleyici bunun yerine yazmış gibi davranacaktır:
- (instancetype)initWithBar:(NSInteger)bar
Bu ARC için gerekliydi. Bu, Clang Dil Uzantıları İlgili sonuç türlerinde açıklanmaktadır . Bu yüzden insanlar sana kullanmanın gerekli olmadığını söyleyecekler instancetype, ama ben bunu yapman gerektiğini savunuyorum. Bu cevabın geri kalanı bununla ilgilidir.
Üç avantajı vardır:
- Açık. Kodunuz başka bir şey yerine, söylediklerini yapıyor.
- Desen. Önemli olan zamanlarda var olan iyi alışkanlıklar inşa ediyorsunuz.
- Tutarlılık. Kodunuzda bir miktar tutarlılık belirlediniz, bu da onu daha okunabilir hale getirdi.
Açık
Bir ülkeden dönmenin teknik bir yararı olmadığı doğrudur . Derleyici otomatik dönüştürür çünkü Ama bu To . Bu tuhaflığa güveniyorsun; bir döndürme yazdığını yazarken , derleyici bunu bir döndürme işlemi olarak yorumluyor .instancetypeinitidinstancetypeinitidinstancetype
Bunlar derleyiciye eşdeğerdir :
- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;
Bunlar gözlerinizle eşdeğer değil. En iyi ihtimalle, farkı görmezden gelmeyi ve üzerinde kaymayı öğreneceksiniz. Bu görmezden gelmeyi öğrenmen gereken bir şey değil.
Desen
Hiçbir farkı olmasa da initve diğer yöntemler, orada olan bir sınıf fabrika tanımlayan en kısa sürede bir fark.
Bu ikisi eşdeğer değildir:
+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
İkinci formu istiyorsunuz. Bir kurucunun instancetypedönüş türü olarak yazmaya alışkınsanız , her seferinde doğru şekilde alırsınız.
Tutarlılık
Son olarak, hepsini bir araya getirdiğinizi düşünün: bir initfonksiyon ve aynı zamanda bir sınıf fabrikası istiyorsunuz .
Eğer kullanırsanız idiçin init, böyle koduyla sonuna kadar:
- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Ancak kullanırsanız instancetype, bunu elde edersiniz:
- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Daha tutarlı ve daha okunabilir. Aynı şeyi geri getiriyorlar ve şimdi bu açık.
Sonuç
Eski derleyiciler için kasıtlı olarak kod yazmadığınız sürece, instancetypeuygun olduğunda kullanmalısınız .
Geri dönen bir mesaj yazmadan önce tereddüt etmelisiniz id. Kendinize sorun: Bu, bu sınıfın bir örneği mi? Eğer öyleyse, bu bir instancetype.
Kesinlikle geri dönmeniz gereken durumlar var id, ancak muhtemelen instancetypeçok daha sık kullanacaksınız .