2011-12-09 20 views
5

Ho questa domanda here (così come altre domande su SO), e la documentazione Apple sulle raccolte Objective-C e l'enumerazione rapida. Ciò che non è chiaro è se un NSArray popolata con diversi tipi, e un ciclo si crea come:Enumerazione rapida su NSArray di tipi diversi

for (NSString *string in myArray) 
    NSLog(@"%@\n", string); 

Cosa succede esattamente qui? Il ciclo salterà su tutto ciò che non è un NSString? Ad esempio, se (per l'argomento) un array UIView si trova nella matrice, cosa accadrebbe quando il ciclo incontra quell'elemento?

+3

veloce enumerazione "prende la tua parola" sulla classe. @awfullyjohn offre la migliore soluzione per gestire un array con membri di classi sconosciute. Non si verifica alcun filtraggio implicito e potrebbe comportare la chiamata di un metodo che il ricevitore non può gestire ... crash –

risposta

9

Perché vuoi farlo? Penso che potrebbe causare comportamenti buggy e non intenzionali. Se l'array è popolato con elementi diversi, utilizzare questo invece:

for (id object in myArray) { 
    // Check what kind of class it is 
    if ([object isKindOfClass:[UIView class]]) { 
     // Do something 
    } 
    else { 
     // Handle accordingly 
    } 
} 

Quello che state facendo nel vostro esempio è effettivamente la stessa,

for (id object in myArray) { 
    NSString *string = (NSString *)object; 
    NSLog(@"%@\n", string); 
} 

Solo perché si lanci object come (NSString *) non lo fa significa che string punta effettivamente a un oggetto NSString. Chiamando il numero NSLog() in questo modo chiameremo il metodo - (NSString *)description in base allo NSObject protocol, a cui potrebbe essere conforme o meno la classe a cui si fa riferimento all'interno dell'array. Se è conforme, lo stamperà. Altrimenti, si bloccherà.

+1

Un buon punto, ma questo non risponde alla domanda. – PengOne

+0

La domanda è più una più profonda comprensione dell'Obiettivo-C che fare qualcosa del genere. Inoltre, ultimamente ho lavorato con C# dove è possibile specificare il tipo che va in una raccolta. –

+1

Vero. Aggiornerò la mia risposta, ma in fondo mi hai battuto. – john

2

Interessante domanda. La sintassi più generica per l'enumerazione veloce è

for (NSObject *obj in myArray) 
    NSLog(@"%@\n", obj); 

credo che facendo

for (NSString *string in myArray) 
    NSLog(@"%@\n", string); 

invece, si sta semplicemente lanciando ogni oggetto come un NSString. Cioè, credo che quanto sopra è equivalente a

for (NSObject *obj in myArray) { 
    NSString *string = obj; 
    NSLog(@"%@\n", string); 
} 

non riuscivo a trovare precisa menzione di questo nella Apple documentation for Fast Enumeration, ma è possibile controllare su un esempio e vedere cosa succede.

+1

La versione "più generica" ​​non sarebbe "for (id obj in arr)" (poiché copre (il numero minuscolo di) oggetti che non discendono da 'NSObject')? –

2

Ho appena provato un esempio veloce ... Ecco il mio codice.

NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:1]; 
NSNumber *number = [NSNumber numberWithInteger:6]; 
[array addObject:number]; 
[array addObject:@"Second"]; 

Ora se registro semplicemente l'oggetto, nessun problema. L'istanza NSNumber viene trasmessa come NSString, ma entrambi i metodi rispondono allo -description, quindi non è un problema.

for (NSString *string in array) 
{ 
    NSLog(@"%@", string); 
} 

Tuttavia, se si tenta di accedere -length su NSString ...

for (NSString *string in array) 
{ 
    NSLog(@"%i", string.length); 
} 

... viene generata NSInvalidArgumentException perché NSNumber non risponde al selettore -length. Per farla breve, Objective-C ti dà un sacco di corda. Non impiccarti con esso.

+0

NSNumber non viene trasmesso come NSString. Sia NSNumber che NSString sono discendenti di NSObject, di cui NSLog chiama 'description', come dici tu. Ma non è coinvolto il casting. –

+0

Oh capisco. Immagino di aver appena confuso il casting con le funzionalità di completamento del codice. Mentre stavo scrivendo l'esempio, non ho ricevuto avvisi riguardo alla messaggistica '-length' su un'istanza di' NSNumber'. –

+0

Ah! Ok ... Adesso capisco anche cosa intendi. Il NSNumber viene castato come NSString in 'string'. Ho pensato che stavi dicendo che NSLog stava facendo il casting (dalla menzione di 'description'). Colpa mia! –

3

Devi capire che un puntatore in obj-c non ha informazioni sul tipo. Anche se scrivi NSString*, è solo un controllo di compilazione. Durante il runtime, tutto è solo un id.

Il runtime Obj-c non verifica mai se gli oggetti appartengono alla classe indicata. È possibile inserire NSNumbers nei puntatori NSString senza problemi. Un errore appare solo quando si tenta di chiamare un metodo (inviare un messaggio) che non è definito sull'oggetto.

Come funziona l'enumerazione veloce? È esattamente la stessa di:


for (NSUInteger i = 0; i < myArray.count; i++) { 
    NSString* string = [myArray objectAtIndex:i]; 

    [...] 
} 

È solo più veloce perché opera a un livello inferiore.

1

Dal rispondere tutti di NSObject a isKindOfClass, si potrebbe ancora mantenere il casting al minimo:

for(NSString *string in myArray) { 
    if (![string isKindOfClass:[NSString class]]) 
     continue; 
    // proceed, knowing you have a valid NSString * 
    // ... 
} 
Problemi correlati