Esistono raccolte fortemente tipizzate in Objective-C?


140

Sono nuovo di programmazione per Mac / iPhone e Objective-C. In C # e Java abbiamo "generici", classi di raccolta i cui membri possono essere solo del tipo dichiarato. Ad esempio, in C #

Dictionary<int, MyCustomObject>

può contenere solo chiavi che sono numeri interi e valori di tipo MyCustomObject. Esiste un meccanismo simile in Objective-C?


Ho appena iniziato a conoscere ObjC me stesso. Forse puoi usare ObjC ++ per eseguire il sollevamento di carichi pesanti?
Toybuilder,

Potresti essere interessato alle risposte a questa domanda: esiste un modo per imporre la digitazione su NSArray, NSMutableArray, ecc.? . Argomenti vengono forniti perché non è una pratica comune in Objective-C / Cocoa.
mouviciel,

2
ObjC ++ non è in realtà un linguaggio ... solo un modo per fare riferimento alla capacità di ObjC di gestire il C ++ in linea esattamente come farebbe con C. Non dovresti farlo a meno che tu non debba, comunque (come se avessi bisogno utilizzare una libreria di terze parti scritta in C ++).
Marc W,

Praticamente un duplicato esatto di stackoverflow.com/questions/649483/…
Barry Wark,

@ Mark W - "non dovrei farlo" perché no? Ho usato ObjC ++ e funziona benissimo. Posso fare #import <map> e @property std :: map <int, NSString *> myDict; Posso usare l'api completa del cacao e avere collezioni fortemente tipizzate. Non vedo alcun lato negativo.
John Henckel,

Risposte:


211

In Xcode 7, Apple ha introdotto 'Lightweight Generics' in Objective-C. In Objective-C, genereranno avvisi del compilatore in caso di mancata corrispondenza del tipo.

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

E nel codice Swift, produrranno un errore del compilatore:

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

I generici leggeri sono destinati all'uso con NSArray, NSDictionary e NSSet, ma puoi anche aggiungerli alle tue classi:

@interface GenericsTest<__covariant T> : NSObject

-(void)genericMethod:(T)object;

@end

@implementation GenericsTest

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

@end

Objective-C si comporterà come prima con gli avvisi del compilatore.

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

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

ma Swift ignorerà completamente le informazioni generiche. (Non è più vero in Swift 3+.)

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

A parte queste classi di raccolta della Fondazione, i generici leggeri di Objective-C vengono ignorati da Swift. Qualsiasi altro tipo che utilizza generici leggeri viene importato in Swift come se non fosse parametrizzato.

Interazione con le API di Objective-C


Dal momento che ho domande su farmaci generici e tipi restituiti nei metodi ho chiesto la mia domanda in thread diverso, per tenere tutto chiaro: stackoverflow.com/questions/30828076/...
LVP

2
@rizzes. Sì, è stato appena introdotto.
Connor,

Un avvertimento qui è che Swift non ignora del tutto le annotazioni del tipo nella tua classe ObjC generica. Se specifichi vincoli, ad esempio MyClass <Foo: id<Bar>>, il tuo codice Swift assumerà che i valori siano il tipo del tuo vincolo, che ti dà qualcosa su cui lavorare. Tuttavia, le sottoclassi specializzate di MyClassavrebbero i loro tipi specializzati ignorati (essere visto esattamente come un generico MyClass). Vedi github.com/bgerstle/LightweightGenericsExample
Brian Gerstle

Quindi questa compilazione per i sistemi operativi 10.10, 10.9 e precedenti?
p0lAris,

Dovrebbe essere impostato il target di distribuzione per supportarli
Connor

91

Questa risposta è obsoleta ma rimane per valore storico. A partire da Xcode 7, la risposta di Connor dell'8 giugno 15 è più accurata.


No, non ci sono generici in Objective-C a meno che tu non voglia usare i modelli C ++ nelle tue classi di raccolta personalizzate (cosa che scoraggio fortemente).

Objective-C ha la tipizzazione dinamica come caratteristica, il che significa che il runtime non si preoccupa del tipo di un oggetto poiché tutti gli oggetti possono ricevere messaggi. Quando si aggiunge un oggetto a una raccolta integrata, vengono semplicemente trattati come se fossero di tipo id. Ma non preoccuparti, basta inviare messaggi a quegli oggetti come al solito; funzionerà bene (a meno che ovviamente uno o più degli oggetti nella raccolta non rispondano al messaggio che stai inviando) .

I generici sono necessari in linguaggi come Java e C # perché sono linguaggi forti e tipicamente statici. Gioco di palla completamente diverso rispetto alla funzionalità di digitazione dinamica di Objective-C.


88
Non sono d'accordo con "non ti preoccupare, basta inviare messaggi a quegli oggetti". Se si inserisce un tipo errato di oggetti nella raccolta, che non rispondono a questi messaggi, si genereranno errori di runtime. L'uso di generici in altre lingue evita questo problema con i controlli del tempo di compilazione.
henning77,

8
@ henning77 Sì, ma Objective-C è un linguaggio più dinamico di queste lingue. Se desideri una sicurezza di tipo elevata, utilizza queste lingue.
Raffi Khatchadourian,

36
Non sono inoltre d'accordo con la filosofia non ti preoccupare, ad esempio se estrai il primo oggetto da un NSArray e lo lanci in un NSNumber ma quell'elemento era davvero un NSString, sei fregato ...
jjxtra,

13
@RaffiKhatchadourian: non c'è molta scelta se stai scrivendo un'app iOS. Se fosse semplice scriverne uno con Java e ottenere tutti i vantaggi della scrittura di un'app nativa, credimi: lo farei.
ericsoco,

11
La più grande lamentela che ho di questo non riguarda i linguaggi dinamici rispetto al controllo del tempo di compilazione, ma la semplice comunicazione con gli sviluppatori. Non posso semplicemente guardare una dichiarazione di proprietà e sapere che tipo di oggetti restituirà a meno che non sia documentato da qualche parte.
devios1

11

No, ma per renderlo più chiaro puoi commentarlo con il tipo di oggetto che vuoi archiviare, l'ho visto fare alcune volte quando devi scrivere qualcosa in Java 1.4 al giorno d'oggi) ad esempio:

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

o

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

Immagino sia un buon modo per documentarlo, nel caso in cui qualcun altro legga il tuo codice. Comunque il nome della variabile dovrebbe essere il più chiaro possibile per sapere quali oggetti contiene.
htafoya,

6

Non ci sono generici in Objective-C.

Dai documenti

Le matrici sono raccolte ordinate di oggetti. Cocoa fornisce diverse classi di array, NSArray, NSMutableArray (una sottoclasse di NSArray) e NSPointerArray.


Il link al documento in risposta è morto - "Spiacenti, non è possibile trovare quella pagina" .
Pang


5

Questo è stato rilasciato in Xcode 7 (finalmente!)

Si noti che nel codice Obiettivo C, è solo un controllo in fase di compilazione; non si verificherà alcun errore di runtime solo per l'inserimento di un tipo errato in una raccolta o l'assegnazione a una proprietà digitata.

Dichiarare:

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

Uso:

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

Fai attenzione a quelli *.


4

I NSArray generici possono essere realizzati eseguendo la sottoclasse NSArraye ridefinendo tutti i metodi forniti con metodi più restrittivi. Per esempio,

- (id)objectAtIndex:(NSUInteger)index

dovrebbe essere ridefinito

@interface NSStringArray : NSArray

come

- (NSString *)objectAtIndex:(NSUInteger)index

affinché un NSArray contenga solo stringhe NSS.

La sottoclasse creata può essere utilizzata come sostituzione drop-in e offre molte funzioni utili: avvisi del compilatore, accesso alle proprietà, migliore creazione di codice e completamento in Xcode. Tutte queste sono funzioni in fase di compilazione, non è necessario ridefinire l'implementazione effettiva: i metodi di NSArray possono ancora essere utilizzati.

È possibile automatizzare questo e ridurlo a sole due affermazioni, il che lo avvicina alle lingue che supportano i generici. Ho creato un'automazione con WMGenericCollection , in cui i modelli sono forniti come macro preprocessore C.

Dopo aver importato il file di intestazione contenente la macro, è possibile creare un NSArray generico con due istruzioni: una per l'interfaccia e una per l'implementazione. Devi solo fornire il tipo di dati che desideri memorizzare e i nomi per le tue sottoclassi. WMGenericCollection fornisce tali modelli per NSArray, NSDictionarye NSSet, così come le loro controparti mutevoli.

Un esempio: List<int>potrebbe essere realizzato da una classe personalizzata chiamata NumberArray, che viene creata con la seguente istruzione:

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

Una volta creato NumberArray, puoi usarlo ovunque nel tuo progetto. Manca la sintassi di <int>, ma è possibile scegliere il proprio schema di denominazione per etichettarli come classi come modelli.




2

Ora i sogni diventano realtà: ci sono Generics in Objective-C da oggi (grazie, WWDC). Non è uno scherzo - sulla pagina ufficiale di Swift:

Le nuove funzionalità di sintassi consentono di scrivere codice più espressivo migliorando la coerenza in tutta la lingua. Gli SDK hanno utilizzato nuove funzionalità di Objective-C come l'annotazione generica e nulla annullabilità per rendere il codice Swift ancora più pulito e sicuro. Ecco solo un esempio dei miglioramenti di Swift 2.0.

E l'immagine che lo dimostra:Generici dell'obiettivo-C


2

Voglio solo saltare qui. Ho scritto un post sul blog qui su Generics.

La cosa che voglio contribuire è che i generici possono essere aggiunti a qualsiasi classe , non solo alle classi di raccolta come indica Apple.

Ho aggiunto con successo quindi a una varietà di classi in quanto funzionano esattamente come le collezioni Apple. vale a dire. compilare il controllo del tempo, il completamento del codice, consentendo la rimozione di cast, ecc.

Godere.


-2

Le classi Collezioni fornite dai framework Apple e GNUStep sono semi-generiche in quanto presuppongono che vengano dati oggetti, alcuni ordinabili e altri che rispondono a determinati messaggi. Per le primitive come float, ints, ecc., Tutta la struttura delle matrici C è intatta e può essere utilizzata e sono disponibili speciali oggetti wrapper da utilizzare nelle classi di raccolta generali (ad es. NSNumber). Inoltre, una classe Collection può essere sottoclassata (o specificamente modificata tramite categorie) per accettare oggetti di qualsiasi tipo, ma è necessario scrivere tutto il codice di gestione del tipo da soli. I messaggi possono essere inviati a qualsiasi oggetto ma devono essere nulli se non sono appropriati per l'oggetto oppure il messaggio deve essere inoltrato a un oggetto appropriato. Gli errori di tipo reale devono essere rilevati in fase di compilazione, non in fase di esecuzione. In fase di esecuzione dovrebbero essere gestiti o ignorati. Infine, Objc fornisce funzionalità di riflessione in fase di runtime per gestire casi difficili e la risposta ai messaggi, il tipo specifico e i servizi possono essere controllati su un oggetto prima che venga inviato un messaggio o inserito in una raccolta inappropriata. Attenzione che librerie e framework diversi adottano convenzioni diverse su come si comportano i loro oggetti quando vengono inviati messaggi per cui non hanno risposte in codice, quindi RTFM. A parte i programmi giocattolo e le build di debug, la maggior parte dei programmi non dovrebbe arrestarsi in modo anomalo a meno che non si rovinino davvero e provino a scrivere dati errati sulla memoria o sul disco, eseguire operazioni illegali (ad esempio dividere per zero, ma è possibile catturare anche quello) o accedere risorse di sistema off-limits. Il dinamismo e il tempo di esecuzione di Objective-C consentono alle cose di fallire con garbo e dovrebbero essere integrate nel codice. (SUGGERIMENTO) se riscontri problemi con la genericità nelle tue funzioni, prova qualche specificità. Scrivi le funzioni con tipi specifici e lascia che il runtime selezioni (ecco perché sono chiamati selettori!) La funzione membro appropriata in fase di esecuzione.

Example:
    -(id) sort (id) obj;  // too generic. catches all.
     // better
    -(id) sort: (EasilySortableCollection*) esc;
    -(id) sort: (HardToSortCollection*) hsc; 
    ...
    [Sorter  sort: MyEasyColl];
    [Sorter  sort: MyHardColl];
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.