Apakah ada koleksi yang sangat diketik di Objective-C?

140

Saya baru mengenal pemrograman Mac / iPhone dan Objective-C. Di C # dan Java kita memiliki "generik", kelas koleksi yang anggotanya hanya dari tipe yang dideklarasikan. Misalnya, dalam C #

Dictionary<int, MyCustomObject>

hanya dapat berisi kunci yang bilangan bulat dan nilai yang bertipe MyCustomObject. Apakah mekanisme serupa ada di Objective-C?

Kaya
sumber
Baru mulai belajar tentang ObjC sendiri. Mungkin Anda bisa menggunakan ObjC ++ untuk melakukan angkat berat?
Toybuilder
Anda mungkin tertarik pada jawaban untuk pertanyaan ini: Apakah ada cara untuk memberlakukan pengetikan pada NSArray, NSMutableArray, dll.? . Argumen diberikan mengapa itu bukan praktik umum di Objective-C / Cocoa.
mouviciel
2
ObjC ++ bukan benar-benar bahasa ... hanya lebih dari cara untuk referensi kemampuan ObjC untuk menangani C ++ inline sama seperti yang akan menangani C. Anda tidak harus melakukan ini kecuali Anda harus melakukannya, meskipun (seperti jika Anda perlu untuk menggunakan perpustakaan pihak ketiga yang ditulis dalam C ++).
Marc W
Cukup banyak duplikat yang tepat dari stackoverflow.com/questions/649483/...
Barry Wark
@ Mark W - "seharusnya tidak melakukan ini" mengapa tidak? Saya telah menggunakan ObjC ++ dan berfungsi dengan baik. Saya dapat melakukan #import <map> dan @property std :: map <int, NSString *> myDict; Saya bisa menggunakan api Kakao penuh DAN memiliki koleksi yang sangat diketik. Saya tidak melihat sisi buruknya.
John Henckel

Jawaban:

211

Di Xcode 7, Apple telah memperkenalkan 'Generasi Ringan' ke Objective-C. Di Objective-C, mereka akan menghasilkan peringatan kompiler jika ada ketidakcocokan tipe.

NSArray<NSString*>* arr = @[@"str"];

NSString* string = [arr objectAtIndex:0];
NSNumber* number = [arr objectAtIndex:0]; // Warning: Incompatible pointer types initializing 'NSNumber *' with an expression of type 'NSString *'

Dan dalam kode Swift, mereka akan menghasilkan kesalahan kompiler:

var str: String = arr[0]
var num: Int = arr[0] //Error 'String' is not convertible to 'Int'

Lightweight Generics dimaksudkan untuk digunakan dengan NSArray, NSDictionary dan NSSet, tetapi Anda juga dapat menambahkannya ke kelas Anda sendiri:

@interface GenericsTest<__covariant T> : NSObject

-(void)genericMethod:(T)object;

@end

@implementation GenericsTest

-(void)genericMethod:(id)object {}

@end

Objective-C akan berperilaku seperti sebelumnya dengan peringatan kompiler.

GenericsTest<NSString*>* test = [GenericsTest new];

[test genericMethod:@"string"];
[test genericMethod:@1]; // Warning: Incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'

tetapi Swift akan mengabaikan informasi Generic sepenuhnya. (Tidak lagi benar dalam Swift 3+.)

var test = GenericsTest<String>() //Error: Cannot specialize non-generic type 'GenericsTest'

Selain dari kelas koleksi Foundation ini, generik ringan Objective-C diabaikan oleh Swift. Jenis lain yang menggunakan obat generik ringan diimpor ke Swift seolah-olah mereka tidakparameter.

Berinteraksi dengan API Objective-C

Connor
sumber
Karena saya memiliki pertanyaan tentang obat generik dan jenis yang dikembalikan dalam metode, saya mengajukan pertanyaan saya di utas yang berbeda, untuk menjaga semuanya tetap jelas: stackoverflow.com/questions/30828076/…
lvp
2
@rizzes. Ya, baru saja diperkenalkan.
Connor
Satu peringatan di sini adalah bahwa Swift tidak sepenuhnya mengabaikan anotasi jenis di kelas ObjC generik Anda. Jika Anda menentukan batasan, misalnya MyClass <Foo: id<Bar>>, kode Swift Anda akan menganggap nilai adalah jenis kendala Anda, yang memberi Anda sesuatu untuk dikerjakan. Namun, subclass khusus dari MyClassjenis khusus mereka akan diabaikan (terlihat efektif sama dengan generik MyClass). Lihat github.com/bgerstle/LightweightGenericsExample
Brian Gerstle
Jadi apakah ini mengkompilasi untuk 10.10, 10.9 dan sistem operasi sebelumnya?
p0lAris
Seharusnya selama Anda menetapkan target penyebaran Anda untuk mendukung mereka
Connor
91

Jawaban ini sudah usang tetapi tetap untuk nilai historis. Pada Xcode 7, jawaban Connor mulai 8 Juni '15 lebih akurat.


Tidak, tidak ada obat generik di Objective-C kecuali Anda ingin menggunakan template C ++ di kelas koleksi kustom Anda sendiri (yang saya sangat tidak menyarankan).

Objective-C memiliki pengetikan dinamis sebagai fitur, yang berarti bahwa runtime tidak peduli dengan jenis objek karena semua objek dapat menerima pesan. Saat Anda menambahkan objek ke koleksi bawaan, mereka hanya diperlakukan seolah-olah mereka diketik id. Tapi jangan khawatir, cukup kirim pesan ke objek-objek seperti biasa; itu akan berfungsi dengan baik (kecuali tentu saja satu atau lebih objek dalam koleksi tidak menanggapi pesan yang Anda kirim) .

Generik diperlukan dalam bahasa seperti Java dan C # karena mereka kuat, bahasa yang diketik secara statis. Ballgame yang benar-benar berbeda dari fitur pengetikan dinamis Objective-C.

Marc W
sumber
88
Saya tidak setuju untuk "jangan khawatir, cukup kirim pesan ke objek-objek itu". Jika Anda memasukkan tipe objek yang salah ke koleksi, yang tidak menanggapi pesan ini, ini akan menghasilkan kesalahan runtime. Menggunakan obat generik dalam bahasa lain menghindari masalah ini dengan mengkompilasi pemeriksaan waktu.
henning77
8
@ henning77 Ya, tapi Objective-C adalah bahasa yang lebih dinamis daripada bahasa-bahasa ini. Jika Anda ingin keamanan jenis yang kuat, gunakan bahasa-bahasa itu.
Raffi Khatchadourian
36
Saya juga tidak setuju dengan filosofi jangan khawatir - misalnya jika Anda menarik item pertama dari NSArray dan melemparkannya ke NSNumber tetapi item itu benar-benar NSString, Anda kacau ...
jjxtra
13
@RaffiKhatchadourian - tidak banyak pilihan jika Anda sedang menulis aplikasi iOS. Jika mudah untuk menulis satu dengan Java, dan mendapatkan semua manfaat dari menulis aplikasi asli, percayalah: Saya akan melakukannya.
ericsoco
11
Keluhan terbesar yang saya miliki tentang ini tidak ada hubungannya dengan bahasa dinamis vs kompilasi waktu memeriksa, tetapi komunikasi pengembang sederhana. Saya tidak bisa hanya melihat deklarasi properti dan tahu jenis objek apa yang akan dikembalikan kecuali didokumentasikan di suatu tempat.
devios1
11

Tidak, tetapi untuk membuatnya lebih jelas Anda dapat berkomentar dengan jenis objek yang ingin Anda simpan, saya telah melihat ini dilakukan beberapa kali ketika Anda perlu menulis sesuatu di Jawa 1.4 saat ini) misalnya:

NSMutableArray* /*<TypeA>*/ arrayName = ....

atau

NSDictionary* /*<TypeA, TypeB>*/ dictionaryName = ...
Mark Rhodes
sumber
Saya kira ini adalah cara yang baik untuk mendokumentasikannya, kalau-kalau ada orang lain yang membaca kode Anda. Bagaimanapun nama variabel harus sejelas mungkin untuk mengetahui objek apa yang dikandungnya.
htafoya
6

Tidak ada obat generik di Objective-C.

Dari Documents

Array adalah koleksi benda yang dipesan. Cocoa menyediakan beberapa kelas array, NSArray, NSMutableArray (subclass dari NSArray), dan NSPointerArray.

Matthew Vines
sumber
Tautan ke doc dalam jawaban sudah mati - "Maaf, halaman itu tidak dapat ditemukan" .
Pang
5

Ini dirilis dalam Xcode 7 (akhirnya!)

Perhatikan bahwa dalam kode Objective C, itu hanya pemeriksaan waktu kompilasi; tidak akan ada kesalahan run-time hanya untuk menempatkan jenis yang salah ke koleksi atau menugaskan ke properti yang diketik.

Menyatakan:

@interface FooClass <T> : NSObject
@property (nonatomic) T prop;
@end

Menggunakan:

FooClass<NSString *> *foo = [[FooClass alloc] init];
NSArray<FooClass<NSString *> *> *fooAry = [NSArray array];

Hati-hati dengan *itu.

Kevin
sumber
4

NSArr generik dapat direalisasikan dengan mensubclassing NSArray, dan mendefinisikan ulang semua metode yang disediakan dengan yang lebih membatasi. Sebagai contoh,

- (id)objectAtIndex:(NSUInteger)index

harus didefinisikan ulang di

@interface NSStringArray : NSArray

sebagai

- (NSString *)objectAtIndex:(NSUInteger)index

untuk NSArray hanya mengandung NSStrings.

Subkelas yang dibuat dapat digunakan sebagai pengganti drop-in dan membawa banyak fitur yang berguna: peringatan kompiler, akses properti, pembuatan kode yang lebih baik dan penyelesaian-di Xcode. Semua ini adalah fitur waktu kompilasi, tidak perlu mendefinisikan ulang implementasi yang sebenarnya - metode NSArray masih dapat digunakan.

Mungkin untuk mengotomatisasi ini dan membuatnya menjadi hanya dua pernyataan, yang membuatnya dekat dengan bahasa yang mendukung obat generik. Saya telah membuat otomatisasi dengan WMGenericCollection , di mana templat disediakan sebagai C Preprocessor Macros.

Setelah mengimpor file header yang berisi makro, Anda bisa membuat NSArray umum dengan dua pernyataan: satu untuk antarmuka dan satu untuk implementasi. Anda hanya perlu memberikan tipe data yang ingin Anda simpan dan nama untuk subclass Anda. WMGenericCollection menyediakan template untuk NSArray, NSDictionarydanNSSet , serta rekanan yang bisa berubah.

Contoh: List<int>dapat direalisasikan oleh kelas kustom yang disebut NumberArray, yang dibuat dengan pernyataan berikut:

WMGENERICARRAY_INTERFACE(NSNumber *, // type of the value class
                         // generated class names
                         NumberArray, MutableNumberArray)

Setelah dibuat NumberArray, Anda dapat menggunakannya di mana saja di proyek Anda. Itu tidak memiliki sintaks <int>, tetapi Anda dapat memilih skema penamaan Anda sendiri untuk label ini sebagai kelas sebagai templat.

wm
sumber
perhatikan bahwa hal yang sama ada di CoreLib: github.com/core-code/CoreLib/blob/master/CoreLib/CoreLib.h#L105
user1259710
2

Sekarang mimpi menjadi kenyataan - ada Generik di Objective-C sejak hari ini (terima kasih, WWDC). Ini bukan lelucon - di halaman resmi Swift:

Fitur sintaks baru memungkinkan Anda menulis kode yang lebih ekspresif sambil meningkatkan konsistensi di seluruh bahasa. SDK telah menggunakan fitur Objective-C baru seperti generik dan nullability annotation untuk membuat kode Swift lebih bersih dan lebih aman. Berikut ini hanya contoh perangkat tambahan Swift 2.0.

Dan gambar yang membuktikan ini:Generik Objective-C

htzfun
sumber
2

Hanya ingin melompat ke sini. Saya sudah menulis posting blog di sini tentang Generics.

Hal yang ingin saya sumbangkan adalah Generik dapat ditambahkan ke kelas mana pun , bukan hanya kelas koleksi seperti yang ditunjukkan Apple.

Saya telah berhasil menambahkan kemudian ke berbagai kelas karena mereka bekerja persis sama dengan koleksi Apple. yaitu. kompilasi pemeriksaan waktu, penyelesaian kode, memungkinkan penghapusan gips, dll.

Nikmati.

drekka
sumber
-2

Kelas-kelas Koleksi yang disediakan oleh kerangka kerja Apple dan GNUStep bersifat semi-generik karena mereka menganggap bahwa mereka diberikan objek, beberapa yang dapat diurutkan dan beberapa yang merespons pesan tertentu. Untuk primitif seperti float, ints, dll, semua struktur array C utuh dan dapat digunakan, dan ada objek pembungkus khusus untuk digunakan dalam kelas koleksi umum (mis. NSNumber). Selain itu, kelas Koleksi mungkin sub-kelas (atau secara khusus dimodifikasi melalui kategori) untuk menerima objek dari jenis apa pun, tetapi Anda harus menulis sendiri semua kode penanganan jenis. Pesan dapat dikirim ke objek apa pun tetapi harus mengembalikan nol jika tidak sesuai untuk objek tersebut, atau pesan tersebut harus diteruskan ke objek yang sesuai. Kesalahan tipe sebenarnya harus ditangkap pada waktu kompilasi, bukan pada saat dijalankan. Pada saat run-time mereka harus ditangani atau diabaikan. Akhirnya, Objc menyediakan fasilitas refleksi run-time untuk menangani kasus rumit dan respons pesan, tipe spesifik, dan layanan dapat diperiksa pada suatu objek sebelum dikirim pesan atau dimasukkan ke dalam koleksi yang tidak pantas. Berhati-hatilah bahwa pustaka dan kerangka kerja yang berbeda mengadopsi konvensi yang berbeda tentang bagaimana objek mereka berperilaku ketika mengirim pesan yang tidak memiliki respons kode, jadi RTFM. Selain program mainan dan build debugging, sebagian besar program tidak harus crash kecuali mereka benar-benar mengacau dan mencoba untuk menulis data buruk ke memori atau disk, melakukan operasi ilegal (mis. Bagi dengan nol, tetapi Anda juga dapat menangkapnya), atau mengaksesnya sumber daya sistem terlarang. Dinamisme dan run-time dari Objective-C memungkinkan hal-hal gagal dengan anggun dan harus dibangun ke dalam kode Anda. (PETUNJUK) jika Anda mengalami masalah dengan kemurahan hati dalam fungsi Anda, coba beberapa spesifisitas. Tulis fungsi-fungsi dengan tipe-tipe tertentu dan biarkan runtime memilih (itulah sebabnya mereka disebut penyeleksi!) Fungsi-anggota yang sesuai pada saat run-time.

Example:
    -(id) sort (id) obj;  // too generic. catches all.
     // better
    -(id) sort: (EasilySortableCollection*) esc;
    -(id) sort: (HardToSortCollection*) hsc; 
    ...
    [Sorter  sort: MyEasyColl];
    [Sorter  sort: MyHardColl];
Chris Reid
sumber