Objective-C'de güçlü yazılan koleksiyonlar var mı?


140

Mac / iPhone programlama ve Objective-C için yeniyim. C # ve Java'da, üyeleri sadece bildirilen türde olabilen "jenerikler" toplama sınıflarımız vardır. Örneğin, C #

Dictionary<int, MyCustomObject>

yalnızca tamsayılar ve MyCustomObject türündeki değerler içerebilir. Objective-C'de benzer bir mekanizma var mı?


ObjC'i kendim öğrenmeye başladım. Belki ağır kaldırma yapmak için ObjC ++ kullanabilirsiniz?
Oyuncak Yapımcısı

Bu sorunun cevaplarıyla ilgilenebilirsiniz: NSArray, NSMutableArray, vb. Üzerinde yazmayı zorunlu kılmanın herhangi bir yolu var mı? . Objective-C / Cocoa'da neden yaygın bir uygulama olmadığı argümanları verilmiştir.
mouviciel

2
ObjC ++ gerçekten bir dil değildir ... ObjC'nin C ++ satır içi ile C ile aynı şekilde başa çıkma yeteneğine referansta bulunmak için daha fazla bir yol gerekir. C ++ ile yazılmış bir üçüncü taraf kitaplığı kullanmak için).
Marc W


@ Mark W - "bunu yapmamalı" neden olmasın? ObjC ++ kullandım ve harika çalışıyor. #İmport <map> ve @property std :: map <int, NSString *> myDict yapabilirim; Kakao apisinin tamamını kullanabilirim ve güçlü tipli koleksiyonlarım var. Aşağı taraf görmüyorum.
John Henckel

Yanıtlar:


211

Xcode 7'de Apple, Objective-C'ye 'Lightweight Generics'i tanıttı. Objective-C'de, bir tür uyuşmazlığı varsa derleyici uyarıları oluştururlar.

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 *'

Ve Swift kodunda, bir derleyici hatası üretecekler:

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

Lightweight Generics'in NSArray, NSDictionary ve NSSet ile kullanılması amaçlanmıştır, ancak bunları kendi sınıflarınıza da ekleyebilirsiniz:

@interface GenericsTest<__covariant T> : NSObject

-(void)genericMethod:(T)object;

@end

@implementation GenericsTest

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

@end

Objective-C derleyici uyarılarıyla daha önce olduğu gibi davranacaktır.

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

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

ancak Swift, Genel bilgileri tamamen göz ardı edecektir. (Artık Swift 3+ için geçerli değil.)

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

Bu Foundation koleksiyon sınıflarının yanı sıra, Objective-C hafif jenerikler Swift tarafından göz ardı edilir. Hafif jenerikler kullanan diğer tüm türler Swift'e parametrelenmemiş gibi içe aktarılır.

Objective-C API'leri ile Etkileşim


Yöntemlerde döndürülen jenerikler ve türler hakkında sorum olduğu için her şeyi açık tutmak için sorumu farklı bir konuya sordum: stackoverflow.com/questions/30828076/…
lvp

2
@rizzes. Evet, yeni tanıtıldı.
Connor

Buradaki bir uyarı, Swift'in genel ObjC sınıfınızdaki tür ek açıklamalarını tamamen görmezden gelmemesi. Kısıtlamalar belirtirseniz, örneğin MyClass <Foo: id<Bar>>, Swift kodunuz değerlerin kısıtlamanızın türü olduğunu varsayar ve bu da size çalışmanız gereken bir şey verir . Bununla birlikte, uzman alt sınıflarının MyClassuzmanlık türleri göz ardı edilir (jenerik ile aynı şekilde etkili bir şekilde görülebilir MyClass). Bkz. Github.com/bgerstle/LightweightGenericsÖrnek
Brian Gerstle

Peki bu 10.10, 10.9 ve önceki işletim sistemleri için derleniyor mu?
p0lAris

Dağıtım hedefinizi destekleyecek şekilde ayarladığınız sürece
Connor

91

Bu cevap eskidir, ancak tarihsel değer için kalır. Xcode 7'den itibaren Connor'un 8 Haz 15'teki cevabı daha doğru.


Hayır, C ++ şablonlarını kendi özel koleksiyon sınıflarınızda kullanmak istemediğiniz sürece Objective-C'de jenerik yoktur (ki kesinlikle önermem).

Objective-C, bir özellik olarak dinamik yazım özelliğine sahiptir, bu da çalışma zamanının tüm nesnelerin mesaj alabileceği için bir nesnenin türünü önemsemediği anlamına gelir. Yerleşik bir koleksiyona bir nesne eklediğinizde, bunlar yalnızca sanki türmiş gibi davranılır id. Ancak endişelenmeyin, normal gibi nesnelere mesaj gönderin; iyi çalışacaktır (elbette koleksiyondaki bir veya daha fazla nesne gönderdiğiniz iletiye yanıt vermiyorsa) .

Java ve C # gibi dillerde jenerikler gereklidir, çünkü bunlar güçlü, statik olarak yazılmış dillerdir. Objective-C'nin dinamik yazma özelliğinden tamamen farklı bir top oyunu.


88
"Merak etmeyin, sadece bu nesnelere mesaj gönderin" e katılmıyorum. Bu iletilere yanıt vermeyen koleksiyona yanlış türde nesneler koyarsanız, çalışma zamanı hataları oluşur. Diğer dillerde jenerik kullanımı derleme zamanı kontrolleri ile bu sorunu önler.
henning77

8
@ henning77 Evet, ancak Objective-C bu dillerden daha dinamik bir dildir. Güçlü tip güvenliği istiyorsanız, bu dilleri kullanın.
Raffi Khatchadourian

36
Endişelenme felsefesine de katılmıyorum - örneğin, ilk öğeyi bir NSArray'den çıkarır ve bir NSNumber'a gönderirseniz, ancak bu öğe gerçekten bir NSString ise, vidalı
olursanız

13
@RaffiKhatchadourian - iOS uygulaması yazıyorsanız çok fazla seçenek yok. Java ile bir tane yazmak ve yerel bir uygulama yazmanın tüm avantajlarından yararlanmak basit olsaydı, inanın bana:
ericsoco

11
Bu konuda sahip olduğum en büyük şikayet, dinamik dillerle derleme zamanı kontrolü değil, basit geliştirici iletişimi ile ilgili değil. Sadece bir özellik bildirimine bakamıyorum ve bir yerde belgelenmedikçe ne tür nesnelerin geri döneceğini bilemiyorum.
devios1 14:13

11

Hayır, ancak daha net hale getirmek için saklamak istediğiniz nesne türüyle yorumlayabilirsiniz, bu günlerde Java 1.4'te bir şeyler yazmanız gerektiğinde birkaç kez yapıldığını gördüm) örneğin:

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

veya

NSDictionary* /*<TypeA, TypeB>*/ dictionaryName = ...

Başka birinin kodunuzu okuması durumunda, bu belgenin belgelenmesi için iyi bir yoldur. Her neyse, hangi nesnenin içerdiğini bilmek için değişkenin adı mümkün olduğunca açık olmalıdır.
htafoya

6

Objective-C'de jenerik yoktur.

Dokümanlar'dan

Diziler sıralı nesne koleksiyonlarıdır. Cocoa, NSArray, NSMutableArray (NSArray'in bir alt sınıfı) ve NSPointerArray gibi çeşitli dizi sınıfları sağlar.


Yanıttaki dokümanın bağlantısı öldü - "Üzgünüz, bu sayfa bulunamadı" .
Pang


5

Bu Xcode 7'de piyasaya sürüldü (sonunda!)

Objective C kodunda sadece derleme zamanı kontrolü olduğunu unutmayın; yalnızca yanlış türü bir koleksiyona yerleştirmek veya yazılan bir özelliğe atamak için çalışma zamanı hatası oluşmaz.

Bildirmek:

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

kullanın:

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

Bunlara dikkat edin *.


4

Genel NSArray'ler, alt sınıflandırma NSArrayve sağlanan tüm yöntemleri daha kısıtlayıcı olanlarla yeniden tanımlayarak gerçekleştirilebilir . Örneğin,

- (id)objectAtIndex:(NSUInteger)index

yeniden tanımlanmalıdır

@interface NSStringArray : NSArray

gibi

- (NSString *)objectAtIndex:(NSUInteger)index

NSArray öğesinin yalnızca NSStrings içermesi için.

Oluşturulan alt sınıf bir yedek değiştirme olarak kullanılabilir ve birçok yararlı özellik getirir: derleyici uyarıları, özellik erişimi, daha iyi kod oluşturma ve Xcode'da tamamlama. Tüm bunlar derleme zamanı özellikleridir, gerçek uygulamayı yeniden tanımlamaya gerek yoktur - NSArray'in yöntemleri hala kullanılabilir.

Bunu otomatikleştirmek ve sadece iki ifadeyle kaynatmak mümkündür, bu da onu jenerikleri destekleyen dillere yakınlaştırır. WMGenericCollection ile bir otomasyon oluşturdum şablonların C Önişlemci Makroları olarak sağlandığı .

Makroyu içeren başlık dosyasını içe aktardıktan sonra, biri arabirim diğeri de uygulama için olmak üzere iki deyimle genel bir NSArray oluşturabilirsiniz. Yalnızca saklamak istediğiniz veri türünü ve alt sınıflarınız için adlar sağlamanız gerekir. WMGenericCollection NSArray, NSDictionaryveNSSet yanı sıra bunların değişken muadilleri.

Bir örnek: aşağıdaki ifadeyle oluşturulan, List<int>adlı özel bir sınıf tarafından gerçekleştirilebilir NumberArray:

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

Oluşturduktan sonra NumberArray, projenizdeki her yerde kullanabilirsiniz. Sözdiziminden yoksundur <int>, ancak bunları şablon olarak sınıf olarak etiketlemek için kendi adlandırma düzeninizi seçebilirsiniz.




2

Şimdi rüyalar gerçekleşiyor - bugünden beri Objective-C'de Generics var (teşekkürler, WWDC). Şaka değil - Swift'in resmi sayfasında :

Yeni sözdizimi özellikleri, dil genelinde tutarlılığı artırırken daha etkileyici kod yazmanıza olanak tanır. SDK'lar, Swift kodunu daha da temiz ve güvenli hale getirmek için jenerikler ve nullabilite notları gibi yeni Objective-C özellikleri kullandılar. İşte Swift 2.0 geliştirmelerinden sadece bir örnek.

Ve bunu kanıtlayan görüntü:Objective-C jenerikleri


2

Sadece buraya atlamak istiyorum. Buraya Generics hakkında bir blog yazısı yazdım .

Katkıda bulunmak istediğim şey, Jeneriklerin herhangi bir sınıfa eklenebilmesidir yalnızca Apple'ın belirttiği gibi koleksiyon sınıflarına değil, .

Apple'ın koleksiyonlarıyla tam olarak aynı şekilde çalıştıklarından sonra çeşitli sınıflara başarıyla ekledim. yani. zaman kontrolü, kod tamamlama, dökümlerin kaldırılmasını sağlama vb.

Zevk almak.


-2

Apple ve GNUStep çerçeveleri tarafından sağlanan Koleksiyonlar sınıfları, bazıları sıralanabilir ve bazıları belirli iletilere yanıt veren nesneler verildiğini varsaymaları nedeniyle yarı jeneriktir. Şamandıralar, ints, vb.Gibi ilkeller için tüm C dizileri yapısı sağlamdır ve kullanılabilir ve genel toplama sınıflarında (örneğin NSNumber) kullanılmak üzere onlar için özel sarıcı nesneler vardır. Ayrıca, bir Collection sınıfı, herhangi bir türdeki nesneleri kabul etmek için alt sınıflara ayrılabilir (veya kategoriler aracılığıyla özel olarak değiştirilebilir), ancak tüm tür işleme kodunu kendiniz yazmanız gerekir. İletiler herhangi bir nesneye gönderilebilir, ancak nesne için uygun değilse null değerini döndürmeli veya ileti uygun bir nesneye iletilmelidir. Gerçek tür hatalar çalışma zamanında değil derleme zamanında yakalanmalıdır. Çalışma zamanında ele alınmalı veya yok sayılmalıdır. Son olarak, Objc zor durumları işlemek için çalışma zamanı yansıma olanakları sağlar ve mesaj yanıtı, belirli tür ve hizmetler, bir ileti gönderilmeden veya uygunsuz bir koleksiyona konulmadan önce bir nesnede kontrol edilebilir. Farklı kütüphanelerin ve çerçevelerin, kod yanıtları olmayan mesajlar gönderildiğinde nesnelerinin nasıl davrandığına ilişkin farklı kurallar benimsediğine dikkat edin, bu nedenle RTFM. Oyuncak programları ve hata ayıklama yapıları dışında, çoğu program gerçekten vidalanıp belleğe veya diske kötü veri yazmaya, yasadışı işlemler gerçekleştirmeye (örn. Sıfıra bölün, ancak bunu da yakalayabilirsiniz) veya erişmedikçe çökmemelidir. sistem dışı kaynaklar. Objective-C dinamizmi ve çalışma zamanı, şeylerin zarif bir şekilde başarısız olmasına izin verir ve kodunuza yerleştirilmelidir. (İPUCU) işlevlerinizde jeneriklikle ilgili sorun yaşıyorsanız, biraz özgüllük deneyin. İşlevleri belirli türlerle yazın ve çalışma zamanının çalışma zamanında uygun üye işlevini seçmesine izin verin (bu yüzden seçiciler denir!).

Example:
    -(id) sort (id) obj;  // too generic. catches all.
     // better
    -(id) sort: (EasilySortableCollection*) esc;
    -(id) sort: (HardToSortCollection*) hsc; 
    ...
    [Sorter  sort: MyEasyColl];
    [Sorter  sort: MyHardColl];
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.