Utas ini agak lama, dan sebagian besar yang ingin saya bagikan sudah ada di sini.
Namun, metode favorit saya tidak disebutkan, dan AFAIK tidak ada dukungan asli di Dentang saat ini, jadi di sini saya pergi ...
Pertama, dan yang terpenting (seperti yang telah ditunjukkan orang lain) kelas abstrak adalah sesuatu yang sangat tidak biasa di Objective-C - kita biasanya menggunakan komposisi (kadang-kadang melalui delegasi) sebagai gantinya. Ini mungkin alasan mengapa fitur seperti itu belum ada dalam bahasa / kompiler - terlepas dari @dynamic
properti, yang IIRC telah ditambahkan dalam ObjC 2.0 yang menyertai pengenalan CoreData.
Tetapi mengingat (setelah menilai situasi Anda dengan cermat!) Anda sampai pada kesimpulan bahwa delegasi (atau komposisi secara umum) tidak cocok untuk menyelesaikan masalah Anda, inilah cara saya melakukannya:
- Menerapkan setiap metode abstrak di kelas dasar.
- Buat implementasi itu
[self doesNotRecognizeSelector:_cmd];
...
- ... diikuti dengan
__builtin_unreachable();
membungkam peringatan yang akan Anda dapatkan untuk metode non-void, memberi tahu Anda "kontrol mencapai fungsi non-void tanpa pengembalian".
- Menggabungkan langkah 2. dan 3. dalam makro, atau membubuhi keterangan
-[NSObject doesNotRecognizeSelector:]
menggunakan __attribute__((__noreturn__))
dalam kategori tanpa implementasi agar tidak menggantikan implementasi asli dari metode itu, dan termasuk header untuk kategori itu dalam PCH proyek Anda.
Saya pribadi lebih suka versi makro karena memungkinkan saya untuk mengurangi boilerplate sebanyak mungkin.
Ini dia:
// Definition:
#define D12_ABSTRACT_METHOD {\
[self doesNotRecognizeSelector:_cmd]; \
__builtin_unreachable(); \
}
// Usage (assuming we were Apple, implementing the abstract base class NSString):
@implementation NSString
#pragma mark - Abstract Primitives
- (unichar)characterAtIndex:(NSUInteger)index D12_ABSTRACT_METHOD
- (NSUInteger)length D12_ABSTRACT_METHOD
- (void)getCharacters:(unichar *)buffer range:(NSRange)aRange D12_ABSTRACT_METHOD
#pragma mark - Concrete Methods
- (NSString *)substringWithRange:(NSRange)aRange
{
if (aRange.location + aRange.length >= [self length])
[NSException raise:NSInvalidArgumentException format:@"Range %@ exceeds the length of %@ (%lu)", NSStringFromRange(aRange), [super description], (unsigned long)[self length]];
unichar *buffer = (unichar *)malloc(aRange.length * sizeof(unichar));
[self getCharacters:buffer range:aRange];
return [[[NSString alloc] initWithCharactersNoCopy:buffer length:aRange.length freeWhenDone:YES] autorelease];
}
// and so forth…
@end
Seperti yang Anda lihat, makro menyediakan implementasi penuh dari metode abstrak, mengurangi jumlah yang diperlukan pelat ke minimum absolut.
Opsi yang lebih baik lagi adalah melobi tim Dentang untuk menyediakan atribut kompiler untuk kasus ini, melalui permintaan fitur. (Lebih baik, karena ini juga akan memungkinkan diagnostik waktu kompilasi untuk skenario di mana Anda mensubclass misalnya NSIncrementalStore.)
Mengapa Saya Memilih Metode Ini
- Ini menyelesaikan pekerjaan secara efisien, dan agak nyaman.
- Ini cukup mudah dimengerti. (Oke, itu
__builtin_unreachable()
mungkin mengejutkan orang, tetapi juga cukup mudah dimengerti.)
- Itu tidak bisa dilucuti dalam rilis rilis tanpa menghasilkan peringatan kompiler lain, atau kesalahan - tidak seperti pendekatan yang didasarkan pada salah satu makro pernyataan.
Poin terakhir itu perlu penjelasan, saya kira:
Beberapa (sebagian besar?) Orang melepaskan pernyataan dalam versi rilis. (Saya tidak setuju dengan kebiasaan itu, tapi itu cerita lain ...) Gagal menerapkan metode yang diperlukan - namun - buruk , mengerikan , salah , dan pada dasarnya akhir dari jagat raya untuk program Anda. Program Anda tidak dapat bekerja dengan benar dalam hal ini karena tidak terdefinisi, dan perilaku tidak terdefinisi adalah hal terburuk yang pernah ada. Oleh karena itu, dapat menghapus diagnostik tersebut tanpa menghasilkan diagnostik baru akan sepenuhnya tidak dapat diterima.
Sudah cukup buruk bahwa Anda tidak dapat memperoleh diagnosa waktu kompilasi yang tepat untuk kesalahan programmer seperti itu, dan harus menggunakan penemuan pada saat run-time untuk hal ini, tetapi jika Anda dapat memplesternya dalam rilis build, mengapa mencoba memiliki kelas abstrak di tempat pertama?