Anda dapat membuat kategori dengan -addSomeClass:metode untuk memungkinkan pemeriksaan tipe statis waktu kompilasi (sehingga kompilator dapat memberi tahu Anda jika Anda mencoba menambahkan objek yang diketahuinya adalah kelas yang berbeda melalui metode itu), tetapi tidak ada cara nyata untuk menerapkannya. sebuah array hanya berisi objek dari kelas tertentu.
Secara umum, sepertinya tidak ada kebutuhan akan batasan seperti itu di Objective-C. Saya rasa saya belum pernah mendengar programmer Cocoa berpengalaman menginginkan fitur itu. Satu-satunya orang yang tampaknya adalah programmer dari bahasa lain yang masih berpikir dalam bahasa tersebut. Jika Anda hanya menginginkan objek dari kelas tertentu dalam larik, hanya tempelkan objek kelas itu di sana. Jika Anda ingin menguji apakah kode Anda berperilaku dengan benar, ujilah.
Saya pikir 'pemrogram Kakao berpengalaman' tidak tahu apa yang mereka lewatkan - pengalaman dengan Java menunjukkan bahwa variabel tipe meningkatkan pemahaman kode dan memungkinkan lebih banyak pemfaktoran ulang.
tgdavies
11
Nah, dukungan Java Generics sangat rusak dengan sendirinya, karena mereka tidak memasukkannya sejak awal ...
dertoni
28
Harus setuju dengan @tgdavies. Saya merindukan kemampuan intellisense dan refactoring yang saya miliki dengan C #. Saat saya menginginkan pengetikan dinamis, saya bisa mendapatkannya di C # 4.0. Ketika saya ingin barang tipe kuat, saya bisa memilikinya juga. Saya telah menemukan bahwa ada waktu dan tempat untuk kedua hal itu.
Steve
18
@charkrit Ada apa dengan Objective-C yang membuatnya 'tidak perlu'? Apakah Anda merasa perlu saat menggunakan C #? Saya mendengar banyak orang mengatakan Anda tidak membutuhkannya di Objective-C tetapi saya pikir orang yang sama ini berpikir Anda tidak membutuhkannya dalam bahasa apa pun, yang membuatnya menjadi masalah preferensi / gaya, bukan kebutuhan.
bacar
17
Bukankah ini tentang mengizinkan kompiler Anda untuk benar-benar membantu Anda menemukan masalah. Tentu Anda bisa mengatakan "Jika Anda hanya ingin objek dari kelas tertentu dalam larik, hanya tempelkan objek kelas itu di sana." Tetapi jika tes adalah satu-satunya cara untuk menerapkannya, Anda berada pada posisi yang kurang menguntungkan. Semakin jauh dari penulisan kode Anda menemukan masalah, semakin mahal masalah itu.
GreenKiwi
145
Belum ada yang meletakkan ini di sini, jadi saya akan melakukannya!
Ini sekarang secara resmi didukung di Objective-C. Pada Xcode 7, Anda dapat menggunakan sintaks berikut:
Penting untuk dicatat bahwa ini hanyalah peringatan kompiler dan secara teknis Anda masih dapat memasukkan objek apa pun ke dalam array Anda. Ada skrip yang tersedia yang mengubah semua peringatan menjadi kesalahan yang akan mencegah pembuatan.
Saya malas di sini, tetapi mengapa ini hanya tersedia di XCode 7? Kita dapat menggunakan nonnulldi XCode 6 dan sejauh yang saya ingat, mereka diperkenalkan pada saat yang bersamaan. Juga, apakah penggunaan konsep tersebut bergantung pada versi XCode atau pada versi iOS?
Guven
@Guven - nullability masuk 6, Anda benar, tetapi obat generik ObjC tidak diperkenalkan sampai Xcode 7.
Logan
Saya cukup yakin ini hanya bergantung pada versi Xcode. Generik hanya peringatan kompilator dan tidak ditunjukkan pada waktu proses. Saya cukup yakin Anda bisa mengkompilasi ke Os apa pun yang Anda inginkan.
Logan
2
@DeanKelly - Anda dapat melakukannya seperti ini: @property (nonatomic, strong) NSArray<id<SomeProtocol>>* protocolObjects; Terlihat sedikit kikuk, tetapi berhasil!
Logan
1
@Logan, tidak hanya ada sekumpulan skrip, yang mencegah pembangunan jika ada peringatan yang terdeteksi. Xcode memiliki mekanisme sempurna bernama "Konfigurasi". Lihat ini boredzo.org/blog/archives/2009-11-07/warnings
adnako
53
Ini adalah pertanyaan yang relatif umum untuk orang-orang yang beralih dari bahasa tipe kuat (seperti C ++ atau Java) ke bahasa dengan tipe lebih lemah atau dinamis seperti Python, Ruby, atau Objective-C. Di Objective-C, sebagian besar objek mewarisi dari NSObject(tipe id) (sisanya mewarisi dari kelas root lain seperti NSProxydan juga bisa berupa tipe id), dan pesan apa pun dapat dikirim ke objek apa pun. Tentu saja, mengirim pesan ke instance yang tidak dikenali dapat menyebabkan error runtime (dan juga akan menyebabkan peringatan compiler.dengan flag -W yang sesuai). Selama sebuah instance menanggapi pesan yang Anda kirim, Anda mungkin tidak peduli kelas itu miliknya. Ini sering disebut sebagai "mengetik bebek" karena "jika dukun seperti bebek [yaitu menanggapi pemilih], itu adalah bebek [yaitu dapat menangani pesan; siapa yang peduli kelas apa]".
Anda dapat menguji apakah sebuah instance merespons pemilih pada waktu proses dengan -(BOOL)respondsToSelector:(SEL)selectormetode tersebut. Dengan asumsi Anda ingin memanggil metode pada setiap instance dalam array tetapi tidak yakin bahwa semua instance dapat menangani pesan tersebut (jadi Anda tidak bisa hanya menggunakan NSArrays -[NSArray makeObjectsPerformSelector:], sesuatu seperti ini akan berfungsi:
for(id o in myArray){if([o respondsToSelector:@selector(myMethod)]){[o myMethod];}}
Jika Anda mengontrol kode sumber untuk instance yang mengimplementasikan metode yang ingin Anda panggil, pendekatan yang lebih umum adalah dengan menentukan a @protocolyang berisi metode tersebut dan mendeklarasikan bahwa kelas yang dimaksud mengimplementasikan protokol itu dalam deklarasi mereka. Dalam penggunaan ini, a @protocolanalog dengan Java Interface atau kelas dasar abstrak C ++. Anda kemudian dapat menguji kesesuaian dengan seluruh protokol daripada menanggapi setiap metode. Dalam contoh sebelumnya, itu tidak akan membuat banyak perbedaan, tetapi jika Anda memanggil beberapa metode, itu mungkin menyederhanakan banyak hal. Contohnya adalah:
for(id o in myArray){if([o conformsToProtocol:@protocol(MyProtocol)]){[o myMethod];}}
dengan asumsi MyProtocolmenyatakan myMethod. Pendekatan kedua ini disukai karena menjelaskan maksud kode lebih dari yang pertama.
Seringkali, salah satu pendekatan ini membebaskan Anda dari kepedulian apakah semua objek dalam array memiliki tipe tertentu. Jika Anda masih peduli, pendekatan bahasa dinamis standar adalah pengujian unit, pengujian unit, pengujian unit. Karena regresi dalam persyaratan ini akan menghasilkan error (kemungkinan tidak dapat dipulihkan) runtime (bukan waktu kompilasi), Anda harus memiliki cakupan pengujian untuk memverifikasi perilaku tersebut sehingga Anda tidak melepaskan crasher ke alam liar. Dalam kasus ini, lakukan operasi yang mengubah array, lalu verifikasi bahwa semua instance dalam array tersebut milik kelas tertentu. Dengan cakupan pengujian yang tepat, Anda bahkan tidak memerlukan tambahan waktu proses untuk memverifikasi identitas instance. Anda memiliki cakupan pengujian unit yang baik, bukan?
Pengujian unit bukanlah pengganti sistem tipe yang layak.
tba
8
Ya, siapa yang membutuhkan perkakas yang bisa dibeli oleh array yang diketik. Saya yakin @BarryWark (dan siapa pun yang telah menyentuh basis kode apa pun yang perlu dia gunakan, baca, pahami, dan dukung) memiliki cakupan kode 100%. Namun saya yakin Anda tidak menggunakan bahan mentah idkecuali jika diperlukan, lebih dari pembuat kode Java menyebarkan Objects. Kenapa tidak? Tidak membutuhkannya jika Anda punya tes unit? Karena itu ada dan membuat kode Anda lebih mudah dipelihara, seperti array yang diketik. Kedengarannya seperti orang-orang yang berinvestasi di platform tidak ingin memberikan poin, dan karena itu menemukan alasan mengapa kelalaian ini sebenarnya bermanfaat.
funkybro
"Mengetik bebek" ?? itu lucu! tidak pernah mendengar hal itu sebelumnya.
John Henckel
11
Anda dapat membuat subkelas NSMutableArrayuntuk menegakkan keamanan tipe.
NSMutableArrayadalah cluster kelas , jadi subclass tidak sepele. Saya akhirnya mewarisi dari NSArraydan meneruskan pemanggilan ke array di dalam kelas itu. Hasilnya adalah kelas yang disebut ConcreteMutableArrayyang merupakan mudah untuk subclass. Inilah yang saya dapatkan:
Bagus tetapi untuk saat ini tidak ada pengetikan yang kuat dengan mengganti beberapa metode. Saat ini hanya pengetikan yang lemah.
Cœur
7
Lihat https://github.com/tomersh/Objective-C-Generics , implementasi generik waktu kompilasi (diimplementasikan preprocessor) untuk Objective-C. Posting blog ini memiliki gambaran yang bagus. Pada dasarnya Anda mendapatkan pemeriksaan waktu kompilasi (peringatan atau kesalahan), tetapi tidak ada hukuman waktu proses untuk obat generik.
Cara yang mungkin bisa dilakukan dengan membuat subclass NSArray tetapi Apple merekomendasikan untuk tidak melakukannya. Lebih mudah untuk berpikir dua kali tentang kebutuhan sebenarnya untuk NSArray yang diketik.
Ini menghemat waktu untuk memiliki pemeriksaan tipe statis pada waktu kompilasi, pengeditan bahkan lebih baik. Sangat membantu saat Anda menulis lib untuk penggunaan jangka panjang.
pinxue
0
Saya membuat subclass NSArray yang menggunakan objek NSArray sebagai backing ivar untuk menghindari masalah dengan sifat class-cluster NSArray. Dibutuhkan blok untuk menerima atau menolak penambahan suatu objek.
untuk hanya mengizinkan objek NSString, Anda dapat mendefinisikan AddBlocksebagai
Anda dapat menentukan a FailBlockuntuk memutuskan apa yang harus dilakukan, jika sebuah elemen gagal dalam pengujian - gagal dengan baik untuk pemfilteran, tambahkan ke larik lain, atau - ini default - buat pengecualian.
VSBlockTestedObjectArray*stringArray =[[VSBlockTestedObjectArray alloc] initWithTestBlock:^BOOL(id element){return[element isKindOfClass:[NSStringclass]];}FailBlock:^(id element){NSLog(@"%@ can't be added, didn't pass the test. It is not an object of class NSString", element);}];VSBlockTestedObjectArray*numberArray =[[VSBlockTestedObjectArray alloc] initWithTestBlock:^BOOL(id element){return[element isKindOfClass:[NSNumberclass]];}FailBlock:^(id element){NSLog(@"%@ can't be added, didn't pass the test. It is not an object of class NSNumber", element);}];[stringArray addObject:@"test"];[stringArray addObject:@"test1"];[stringArray addObject:[NSNumber numberWithInt:9]];[stringArray addObject:@"test2"];[stringArray addObject:@"test3"];[numberArray addObject:@"test"];[numberArray addObject:@"test1"];[numberArray addObject:[NSNumber numberWithInt:9]];[numberArray addObject:@"test2"];[numberArray addObject:@"test3"];NSLog(@"%@", stringArray);NSLog(@"%@", numberArray);
Ini hanyalah contoh kode dan tidak pernah digunakan dalam aplikasi dunia nyata. untuk melakukannya mungkin perlu lebih banyak metode NSArray yang diimplementasikan.
Jika Anda mencampur c ++ dan objektif-c (yaitu menggunakan tipe file mm), Anda dapat memaksakan pengetikan menggunakan pair atau tuple. Misalnya, dalam metode berikut, Anda dapat membuat objek C ++ berjenis std :: pair, mengonversinya menjadi objek berjenis OC wrapper (pembungkus std :: pair yang perlu Anda definisikan), lalu meneruskannya ke beberapa metode OC lainnya, di mana Anda perlu mengubah objek OC kembali ke objek C ++ untuk menggunakannya. Metode OC hanya menerima tipe pembungkus OC, sehingga memastikan keamanan tipe. Anda bahkan dapat menggunakan tuple, template variadic, typelist untuk memanfaatkan fitur C ++ yang lebih canggih untuk memfasilitasi keamanan tipe.
Jawaban:
Anda dapat membuat kategori dengan
-addSomeClass:
metode untuk memungkinkan pemeriksaan tipe statis waktu kompilasi (sehingga kompilator dapat memberi tahu Anda jika Anda mencoba menambahkan objek yang diketahuinya adalah kelas yang berbeda melalui metode itu), tetapi tidak ada cara nyata untuk menerapkannya. sebuah array hanya berisi objek dari kelas tertentu.Secara umum, sepertinya tidak ada kebutuhan akan batasan seperti itu di Objective-C. Saya rasa saya belum pernah mendengar programmer Cocoa berpengalaman menginginkan fitur itu. Satu-satunya orang yang tampaknya adalah programmer dari bahasa lain yang masih berpikir dalam bahasa tersebut. Jika Anda hanya menginginkan objek dari kelas tertentu dalam larik, hanya tempelkan objek kelas itu di sana. Jika Anda ingin menguji apakah kode Anda berperilaku dengan benar, ujilah.
sumber
Belum ada yang meletakkan ini di sini, jadi saya akan melakukannya!
Ini sekarang secara resmi didukung di Objective-C. Pada Xcode 7, Anda dapat menggunakan sintaks berikut:
Catatan
Penting untuk dicatat bahwa ini hanyalah peringatan kompiler dan secara teknis Anda masih dapat memasukkan objek apa pun ke dalam array Anda. Ada skrip yang tersedia yang mengubah semua peringatan menjadi kesalahan yang akan mencegah pembuatan.
sumber
nonnull
di XCode 6 dan sejauh yang saya ingat, mereka diperkenalkan pada saat yang bersamaan. Juga, apakah penggunaan konsep tersebut bergantung pada versi XCode atau pada versi iOS?@property (nonatomic, strong) NSArray<id<SomeProtocol>>* protocolObjects;
Terlihat sedikit kikuk, tetapi berhasil!Ini adalah pertanyaan yang relatif umum untuk orang-orang yang beralih dari bahasa tipe kuat (seperti C ++ atau Java) ke bahasa dengan tipe lebih lemah atau dinamis seperti Python, Ruby, atau Objective-C. Di Objective-C, sebagian besar objek mewarisi dari
NSObject
(tipeid
) (sisanya mewarisi dari kelas root lain sepertiNSProxy
dan juga bisa berupa tipeid
), dan pesan apa pun dapat dikirim ke objek apa pun. Tentu saja, mengirim pesan ke instance yang tidak dikenali dapat menyebabkan error runtime (dan juga akan menyebabkan peringatan compiler.dengan flag -W yang sesuai). Selama sebuah instance menanggapi pesan yang Anda kirim, Anda mungkin tidak peduli kelas itu miliknya. Ini sering disebut sebagai "mengetik bebek" karena "jika dukun seperti bebek [yaitu menanggapi pemilih], itu adalah bebek [yaitu dapat menangani pesan; siapa yang peduli kelas apa]".Anda dapat menguji apakah sebuah instance merespons pemilih pada waktu proses dengan
-(BOOL)respondsToSelector:(SEL)selector
metode tersebut. Dengan asumsi Anda ingin memanggil metode pada setiap instance dalam array tetapi tidak yakin bahwa semua instance dapat menangani pesan tersebut (jadi Anda tidak bisa hanya menggunakanNSArray
s-[NSArray makeObjectsPerformSelector:]
, sesuatu seperti ini akan berfungsi:Jika Anda mengontrol kode sumber untuk instance yang mengimplementasikan metode yang ingin Anda panggil, pendekatan yang lebih umum adalah dengan menentukan a
@protocol
yang berisi metode tersebut dan mendeklarasikan bahwa kelas yang dimaksud mengimplementasikan protokol itu dalam deklarasi mereka. Dalam penggunaan ini, a@protocol
analog dengan Java Interface atau kelas dasar abstrak C ++. Anda kemudian dapat menguji kesesuaian dengan seluruh protokol daripada menanggapi setiap metode. Dalam contoh sebelumnya, itu tidak akan membuat banyak perbedaan, tetapi jika Anda memanggil beberapa metode, itu mungkin menyederhanakan banyak hal. Contohnya adalah:dengan asumsi
MyProtocol
menyatakanmyMethod
. Pendekatan kedua ini disukai karena menjelaskan maksud kode lebih dari yang pertama.Seringkali, salah satu pendekatan ini membebaskan Anda dari kepedulian apakah semua objek dalam array memiliki tipe tertentu. Jika Anda masih peduli, pendekatan bahasa dinamis standar adalah pengujian unit, pengujian unit, pengujian unit. Karena regresi dalam persyaratan ini akan menghasilkan error (kemungkinan tidak dapat dipulihkan) runtime (bukan waktu kompilasi), Anda harus memiliki cakupan pengujian untuk memverifikasi perilaku tersebut sehingga Anda tidak melepaskan crasher ke alam liar. Dalam kasus ini, lakukan operasi yang mengubah array, lalu verifikasi bahwa semua instance dalam array tersebut milik kelas tertentu. Dengan cakupan pengujian yang tepat, Anda bahkan tidak memerlukan tambahan waktu proses untuk memverifikasi identitas instance. Anda memiliki cakupan pengujian unit yang baik, bukan?
sumber
id
kecuali jika diperlukan, lebih dari pembuat kode Java menyebarkanObject
s. Kenapa tidak? Tidak membutuhkannya jika Anda punya tes unit? Karena itu ada dan membuat kode Anda lebih mudah dipelihara, seperti array yang diketik. Kedengarannya seperti orang-orang yang berinvestasi di platform tidak ingin memberikan poin, dan karena itu menemukan alasan mengapa kelalaian ini sebenarnya bermanfaat.Anda dapat membuat subkelas
NSMutableArray
untuk menegakkan keamanan tipe.NSMutableArray
adalah cluster kelas , jadi subclass tidak sepele. Saya akhirnya mewarisi dariNSArray
dan meneruskan pemanggilan ke array di dalam kelas itu. Hasilnya adalah kelas yang disebutConcreteMutableArray
yang merupakan mudah untuk subclass. Inilah yang saya dapatkan:Pembaruan: checkout posting blog ini dari Mike Ash tentang subclass cluster kelas.
Sertakan file tersebut di proyek Anda, lalu buat jenis apa pun yang Anda inginkan dengan menggunakan makro:
MyArrayTypes.h
MyArrayTypes.m
Pemakaian:
Pikiran Lainnya
NSArray
untuk mendukung serialisasi / deserializationBergantung pada selera Anda, Anda mungkin ingin mengganti / menyembunyikan metode umum seperti
- (void) addObject:(id)anObject
sumber
Lihat https://github.com/tomersh/Objective-C-Generics , implementasi generik waktu kompilasi (diimplementasikan preprocessor) untuk Objective-C. Posting blog ini memiliki gambaran yang bagus. Pada dasarnya Anda mendapatkan pemeriksaan waktu kompilasi (peringatan atau kesalahan), tetapi tidak ada hukuman waktu proses untuk obat generik.
sumber
Proyek Github ini mengimplementasikan fungsi itu dengan tepat.
Anda kemudian dapat menggunakan
<>
tanda kurung, seperti yang Anda lakukan di C #.Dari contoh mereka:
sumber
Cara yang mungkin bisa dilakukan dengan membuat subclass NSArray tetapi Apple merekomendasikan untuk tidak melakukannya. Lebih mudah untuk berpikir dua kali tentang kebutuhan sebenarnya untuk NSArray yang diketik.
sumber
Saya membuat subclass NSArray yang menggunakan objek NSArray sebagai backing ivar untuk menghindari masalah dengan sifat class-cluster NSArray. Dibutuhkan blok untuk menerima atau menolak penambahan suatu objek.
untuk hanya mengizinkan objek NSString, Anda dapat mendefinisikan
AddBlock
sebagaiAnda dapat menentukan a
FailBlock
untuk memutuskan apa yang harus dilakukan, jika sebuah elemen gagal dalam pengujian - gagal dengan baik untuk pemfilteran, tambahkan ke larik lain, atau - ini default - buat pengecualian.VSBlockTestedObjectArray.h
VSBlockTestedObjectArray.m
Gunakan seperti:
Ini hanyalah contoh kode dan tidak pernah digunakan dalam aplikasi dunia nyata. untuk melakukannya mungkin perlu lebih banyak metode NSArray yang diimplementasikan.
sumber
Jika Anda mencampur c ++ dan objektif-c (yaitu menggunakan tipe file mm), Anda dapat memaksakan pengetikan menggunakan pair atau tuple. Misalnya, dalam metode berikut, Anda dapat membuat objek C ++ berjenis std :: pair, mengonversinya menjadi objek berjenis OC wrapper (pembungkus std :: pair yang perlu Anda definisikan), lalu meneruskannya ke beberapa metode OC lainnya, di mana Anda perlu mengubah objek OC kembali ke objek C ++ untuk menggunakannya. Metode OC hanya menerima tipe pembungkus OC, sehingga memastikan keamanan tipe. Anda bahkan dapat menggunakan tuple, template variadic, typelist untuk memanfaatkan fitur C ++ yang lebih canggih untuk memfasilitasi keamanan tipe.
sumber
dua sen saya agar sedikit "lebih bersih":
gunakan typedefs:
dalam kode yang bisa kita lakukan:
sumber