2010-01-19 2 views
7

Questo produce un oggetto stringa immutabile:Cacao: test per scoprire se una NSString è immutabile o mutabile?

NSString* myStringA = @"A"; //CORRECTED FROM: NSMutableString* myStringA = @"A"; 

Questo produce un oggetto stringa mutabile:

NSMutableString* myStringB = [NSMutableString stringWithString:@"B"]; 

Ma entrambi gli oggetti sono riportati come lo stesso tipo di oggetto, "NSCFString":

NSLog(@"myStringA is type: %@, myStringB is type: %@", 
[myStringA class], [myStringB class]); 

Quindi cosa distingue questi oggetti internamente e come faccio a verificarli, in modo da poter determinare facilmente se una variabile di stringa misteriosa è immutata abile o mutevole prima di fare qualcosa di malvagio?

+0

Il codice di Philippe sotto di - if ([myStringB isKindOfClass: [NSMutableString class]]) - risolve il problema pratico. – StringSection

+0

Sono ancora curioso di sapere come la differenza tra una stringa immutabile e mutabile sia rappresentata internamente e se ciò possa essere rilevato direttamente (il "tipo di oggetto" reale stampato con NSLog). – StringSection

+1

Correzione: mi sono sbagliato, il codice - if ([myStringB isKindOfClass: [NSMutableString class]]) - non funziona dopotutto. Restituisce vero se la stringa è NSString o NSMutableString. Come indica Philippe di seguito (nella sua risposta modificata con codice diverso) apparentemente non esiste un modo documentato per rilevare oggetti mutabili e immutabili in generale in fase di esecuzione. – StringSection

risposta

4

Non esiste un modo (documentato) per determinare se una stringa è mutabile in fase di esecuzione o meno.

Ci si aspetterebbe una delle seguenti avrebbe funzionato, ma nessuno di loro lavoro:

[[s class] isKindOfClass:[NSMutableString class]]; // always returns false 
[s isMemberOfClass:[NSMutableString class]]; // always returns false 
[s respondsToSelector:@selector(appendString)]; // always returns true 

Maggiori informazioni qui, anche se non ti aiuta con il problema:

http://www.cocoabuilder.com/archive/cocoa/111173-mutability.html

+0

Non funziona: NSMutableString * lala = @ "lala"; \t if ([lala isKindOfClass: [NSMutableString class]]) NSLog (@ "Classe mutevole"); - mostra il messaggio di log, tuttavia l'oggetto è immutabile – Vladimir

+0

Ovviamente non funziona, perché il codice è sbagliato. ** NSMutableString * lala = @ "lala" ** è un codice illegale. –

+0

Scusate ragazzi - NSMutableString * myStringA = @ "A"; era un errore di battitura - ora corretto nel messaggio originale. – StringSection

3

Se si desidera verificare gli scopi di debug, dovrebbe funzionare il seguente codice. La copia su un oggetto immutabile è essa stessa, mentre è una copia vera per i tipi mutabili, su cui si basa il codice. Nota che dal momento che sta chiamando copy è lento, ma dovrebbe andare bene per il debug. Se desideri verificare altri motivi rispetto al debug, vedi la risposta di Rob (e dimenticatene).

BOOL isMutable(id object) 
{ 
    id copy = [object copy]; 
    BOOL copyIsADifferentObject = (copy != object); 
    [copy release]; 
    return copyIsADifferentObject; 
} 

Disclaimer: ovviamente non v'è alcuna garanzia che la copia è equivalente con trattenere per i tipi immutabili. Si può essere sicuri che se isMutable restituisce NO, allora non è mutabile, quindi la funzione dovrebbe essere probabilmente denominata canBeMutable. Nel mondo reale, tuttavia, è un assunto abbastanza sicuro che i tipi immutabili (NSString, NSArray) implementeranno questa ottimizzazione. C'è un sacco di codice incluso le cose di base come NSDictionary che si aspetta una copia veloce da tipi immutabili.

12

I documenti includono una spiegazione abbastanza lunga sul motivo per cui Apple non vuole che tu lo faccia e perché non lo supportano esplicitamente in Receiving Mutable Objects. La sintesi è:

Quindi non prendere una decisione su oggetto mutevolezza in base a ciò introspezione ti dice di un oggetto. Considera gli oggetti come modificabili o non basati su limiti che si sono passati ai limiti dell'API (ovvero, in base al tipo di ritorno ). Se è necessario specificare un oggetto come mutabile o immutabile quando lo si passa ai client, passare tali informazioni come un flag insieme all'oggetto.

Trovo che il loro esempio NSView sia il più semplice da comprendere e illustra un problema base di Cocoa. Hai un NSMutableArray chiamato "elementi" che vuoi esporre come array, ma non vuoi che i chiamanti facciano i conti con. Sono disponibili diverse opzioni:

  1. Esporre il NSMutableArray come un NSArray.
  2. Quando richiesto, effettuare sempre una copia non modificabile
  3. Memorizzare gli elementi come un NSArray e creare un nuovo array ogni volta che viene mutato.

Ho fatto tutto questo in vari punti. # 1 è di gran lunga la soluzione più semplice e veloce. È anche pericoloso, dal momento che l'array potrebbe mutare dietro la schiena del chiamante. Ma Apple indica che è quello che fanno in alcuni casi (si noti l'avviso per -subviews in NSView). Posso confermare che mentre # 2 e # 3 sono molto più sicuri, possono creare importanti problemi di prestazioni, che è probabilmente il motivo per cui Apple ha scelto di non usarli su membri spesso accessibili come -subviews.

Il risultato di tutto questo è che se si utilizza il n. 1, l'introspezione ti può fuorviare. Hai un cast di NSMutableArray come NSArray e l'introspezione indica che è mutabile (l'introspezione non ha modo di sapere altrimenti). Ma non devi mutarlo. Solo il controllo del tipo in fase di compilazione può dirlo, e quindi è l'unica cosa di cui ti puoi fidare.

La correzione per questo sarebbe una sorta di immutabile versione copy-on-write veloce di una struttura dati mutevole. In questo modo # 2 potrebbe essere fatto con prestazioni decenti. Posso immaginare cambiamenti al cluster NSArray che permetterebbero questo, ma non esiste al Cocoa oggi (e potrebbe avere un impatto sulle prestazioni di NSArray nel caso normale, rendendolo un non-starter). Anche se ce l'avessimo, probabilmente c'è troppo codice là fuori che si basa sul comportamento corrente per consentire l'introspezione della mutabilità.

+1

È possibile risolvere il problema delle prestazioni con # 3 implementando ed esponendo gli accessori di matrice mutanti (http://developer.apple.com/documentation/Cocoa/Conceptual/ModelObjects/Articles/moAccessorMethods.html) e utilizzandoli in altre classi. In questo modo, l'accesso diretto alla matrice mutabile rimane privato, ma non stai creando e buttando via array temporanei. –

+1

@PH Questo è vero e avrei dovuto coprire la soluzione KVC. Il problema che pone è che ci sono molti casi in cui il chiamante ha davvero bisogno di una collezione (di solito per passare a qualcos'altro che ne richiede uno). Ho spesso risolto questo problema esponendo qualcosa come elementEnumerator, che il chiamante può trasformare in un'istantanea se lo desidera. Il problema con questo approccio è la quantità di codice aggiuntivo richiesto sia per il chiamante che per il chiamato. È illuminante che Apple, anche nel nuovo codice di UIView, abbia scelto di esporre il NSMutableArray "subviews" come un NSArray e di non fornire un KVC completo. –

+0

Hm? Non sono chiaro su quale problema stai parlando o quale sia la tua soluzione. Post di blog e/o pastebin? –