137

Jestem nowy w programowaniu komputerów Mac/iPhone i Objective-C. W języku C# i Java mamy "generics", klasy kolekcji, których członkowie mogą być tylko typu zadeklarowanego. Na przykład, w języku C#Czy w Objective-C istnieją mocno wpisane kolekcje?

Dictionary<int, MyCustomObject>

może zawierać tylko klucze, które są liczbami całkowitymi i wartości, które są typu MyCustomObject. Czy podobny mechanizm istnieje w Objective-C?

+0

Właśnie zaczynam uczyć się o ObjC. Być może możesz użyć ObjC++ do ciężkiego podnoszenia? – Toybuilder

+2

ObjC++ nie jest tak naprawdę językiem ... po prostu sposobem na odniesienie się do zdolności ObjC do obsługi inline C++, tak samo jak w przypadku obsługi C.Nie powinieneś tego robić, chyba że musisz, (np. Jeśli potrzebujesz biblioteki innej firmy, która została napisana w C++). –

+0

Prawie dokładny duplikat http://stackoverflow.com/questions/649483/is-there-any-way-to-enforce-typing-on-nsarray-nsmutablearray-etc –

Odpowiedz

205

W Xcode 7 Apple wprowadził "Lightweight Generics" do Celu-C. W Objective-C generują ostrzeżenia kompilatora, jeśli występuje niezgodność typów.

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

I w kodzie Swift, będzie produkować błąd kompilatora:

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

Lekkie Leki generyczne są przeznaczone do stosowania z NSArray, NSDictionary i NSSet, ale można też dodawać je do swoich własnych klas :

@interface GenericsTest<__covariant T> : NSObject 

-(void)genericMethod:(T)object; 

@end 

@implementation GenericsTest 

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

@end 

Objective-C zachowuje się tak jak wcześniej z ostrzeżeniami kompilatora.

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

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

, ale Swift całkowicie zignoruje ogólne informacje. (Nie jest już prawdą w Swift 3+.)

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

Aside from than these Foundation collection classes, Objective-C lightweight generics are ignored by Swift. Any other types using lightweight generics are imported into Swift as if they were unparameterized.

Interacting with Objective-C APIs

+0

Ponieważ mam pytanie dotyczące generycznych i typów zwracanych w metodach zadałem moje pytanie w innym wątku, aby wszystko było jasne: http://stackoverflow.com/questions/30828076/objective-c-generics-not-working-for-methods – lvp

+0

Czy możesz używać tego tylko z Xcode 7? – rizzes

+2

@rizzes. Tak, został właśnie wprowadzony. – connor

6

Nie ma generycznych w Objective-C.

From the Docs

Arrays are ordered collections of objects. Cocoa provides several array classes, NSArray, NSMutableArray (a subclass of NSArray), and NSPointerArray.

91

This answer is outdated but remains for historical value. As of Xcode 7, Connor's answer from Jun 8 '15 is more accurate.


Nie, nie ma żadnych leków generycznych w Objective-C, chyba że chcesz używać szablonów C++ we własnych klasach zwyczaj zbierania (co zdecydowanie zniechęcać).

Objective-C ma dynamiczne pisanie jako funkcję, co oznacza, że ​​środowisko wykonawcze nie dba o typ obiektu, ponieważ wszystkie obiekty mogą odbierać wiadomości. Po dodaniu obiektu do wbudowanej kolekcji są one traktowane tak, jakby były typu id. Ale nie martw się, po prostu wysyłaj wiadomości do tych obiektów, jak normalne; będzie działać dobrze (chyba że jeden lub więcej obiektów w kolekcji nie odpowiada na wiadomość, którą wysyłasz).

Generyczne są potrzebne w językach takich jak Java i C#, ponieważ są silnymi, statycznie napisanymi językami. Zupełnie inna gra w piłkę niż funkcja dynamicznego pisania Objective-C.

+87

Nie zgadzam się na "nie martw się, po prostu wysyłaj wiadomości do tych obiektów". Jeśli wstawisz niewłaściwy typ obiektów do kolekcji, które nie odpowiedzą na te wiadomości, spowoduje to błędy w czasie wykonywania. Używanie generycznych w innych językach pozwala uniknąć tego problemu podczas sprawdzania czasu kompilacji. – henning77

+8

@ henning77 Tak, ale Objective-C jest językiem bardziej dynamicznym niż te. Jeśli chcesz mieć silne bezpieczeństwo, użyj tych języków. –

+36

Nie zgadzam się również z filozofią "nie martw się" - na przykład, jeśli wyciągniesz pierwszy element z NSArray i wyrzucisz go do NSNumber, ale ten przedmiot był naprawdę NSStringiem, jesteś wkręcony ... – jjxtra

11

No, ale żeby to wyraźniej można skomentować go z typem obiektu, który chcesz zapisać, widziałem to zrobić kilka razy, kiedy trzeba napisać coś w Javie 1.4 dzisiejszych czasach), np:

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

lub

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

Sądzę, że to dobry sposób na udokumentowanie tego na wypadek, gdyby ktoś inny odczytał twój kod. W każdym razie nazwa zmiennej powinna być tak jasna, jak to tylko możliwe, aby wiedzieć, jakie obiekty zawiera. – htafoya

-2

zbiorów klas przedstawionych przez ram Apple i gnustep są semi-generic tym, że zakładamy, że są one podane przedmioty, niektóre są sortable a niektórzy, że odpowiedzi na niektóre wiadomości. W przypadku prymitywów, takich jak floats, int, itd., Cała struktura tablic C jest nienaruszona i może być użyta, a dla nich istnieją specjalne obiekty opakowaniowe do wykorzystania w ogólnych klasach kolekcji (np. NSNumber). Ponadto, klasa kolekcji może być podklasy (lub specjalnie zmodyfikowana przez kategorie), aby akceptować obiekty dowolnego typu, ale sam musisz napisać cały kod obsługi typu. Wiadomości mogą być wysyłane do dowolnego obiektu, ale powinny zwracać wartość pustą, jeśli są nieodpowiednie dla obiektu, lub wiadomość powinna zostać przekazana do odpowiedniego obiektu. Błędy prawdziwego typu powinny być przechwytywane podczas kompilacji, a nie podczas wykonywania. W czasie wykonywania powinny być obsługiwane lub ignorowane. Wreszcie, Objc zapewnia narzędzia do analizy w czasie rzeczywistym do obsługi trudnych przypadków i odpowiedzi wiadomości, określonego typu i usług można sprawdzić na obiekcie przed wysłaniem wiadomości lub umieszczeniem w nieodpowiedniej kolekcji. Należy pamiętać, że odmienne biblioteki i frameworki przyjmują różne konwencje dotyczące zachowania ich obiektów podczas wysyłania wiadomości, dla których nie mają odpowiedzi na kod, czyli RTFM. Inne niż programy zabawkowe i kompilacje debugowania, większość programów nie powinna się zawieszać, chyba że naprawdę zepsują i spróbują zapisać złe dane w pamięci lub na dysku, wykonają nielegalne operacje (np. Dzielą przez zero, ale możesz też to złapać) lub dostęp niedostępne zasoby systemowe. Dynamika i czas działania Objective-C pozwala na niepowodzenie rzeczy i powinien być wbudowany w twój kod. (WSKAZÓWKA) jeśli masz problemy z generycznością w swoich funkcjach, spróbuj jakiejś specyfiki. Zapisz funkcje w określonych typach i pozwól, aby środowisko wykonawcze wybrało (dlatego nazywa się je selektorami!) Odpowiednią funkcję-członka w czasie wykonywania.

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

Generic NSArrays mogą być realizowane przez instacji NSArray i przedefiniowania wszystkich przewidzianych metod z nich bardziej restrykcyjnych. Na przykład,

- (id)objectAtIndex:(NSUInteger)index 

musiałaby być na nowo w

@interface NSStringArray : NSArray 

jak

- (NSString *)objectAtIndex:(NSUInteger)index 

dla NSArray zawierać tylko NSStrings.

Utworzona podklasa może być używana jako zamiennik i zapewnia wiele użytecznych funkcji: ostrzeżenia kompilatora, dostęp do właściwości, lepsze tworzenie kodu i kończenie w Xcode. Wszystko to są funkcje kompilacyjne, nie ma potrzeby redefiniować rzeczywistej implementacji - metody NSArray mogą być nadal używane.

Można zautomatyzować to i sprowadzić tylko do dwóch instrukcji, co przybliża je do języków, które obsługują generyczne. Stworzyłem automatyzację z WMGenericCollection, w której szablony są dostarczane jako makra preprocesora C.

Po zaimportowaniu pliku nagłówkowego zawierającego makro, można utworzyć ogólny NSArray z dwoma instrukcjami: jeden dla interfejsu i jeden dla implementacji. Musisz tylko podać typ danych, który chcesz przechowywać, oraz nazwy dla twoich podklas. WMGenericCollection dostarcza takie szablony dla NSArray, NSDictionary i NSSet, jak również ich zmienne odpowiedniki.

przykład: List<int> może być realizowana za pomocą niestandardowej klasy o nazwie NumberArray, który jest tworzony z następującym stwierdzeniem:

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

Po utworzeniu NumberArray, można go używać wszędzie w projekcie. Brakuje w nim składni <int>, ale możesz wybrać własny schemat nazewnictwa, aby oznaczyć je jako klasy jako szablony.

+0

Zauważ, że to samo istnieje w CoreLib: https://github.com/core-code/CoreLib/blob/master/CoreLib/CoreLib.h#L105 – user1259710

2

Teraz marzenia się spełniają - są generics w Objective- C od dzisiaj (dzięki, WWDC). To nie żart - na official page Swift:

New syntax features let you write more expressive code while improving consistency across the language. The SDKs have employed new Objective-C features such as generics and nullability annotation to make Swift code even cleaner and safer. Here is just a sampling of Swift 2.0 enhancements.

i wizerunek, że dowody to: Objective-C generics

5

This was released in Xcode 7

Należy zauważyć, że w Celu kodu C, to tylko compile- (wreszcie!) kontrola czasu; nie będzie błędu w czasie wykonywania tylko dla umieszczenia niewłaściwego typu w kolekcji lub przypisaniu do wpisanej właściwości.

Declare:

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

Zastosowanie:

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

Bądź ostrożny z tymi * s.

+0

Piękna. :) Dziękuję za aktualizację. – Qix

2

Po prostu chcę tu wskoczyć. Napisałem wpis na blogu o numerze over here o produkcie Generics.

To, co chcę wnieść, to że Generics można dodać do dowolnej klasy, a nie tylko do klas kolekcji, jak wskazuje Apple.

Udało mi się dodać do różnych klas, ponieważ działają dokładnie tak samo, jak robią to kolekcje Apple. to znaczy. Sprawdzanie czasu kompilacji, uzupełnianie kodu, umożliwienie usunięcia odlewów itp.

Ciesz się.