2009-09-28 15 views
8

La mia comprensione è che entrambi creano una NSMutableString, solo la prima è di proprietà del sistema e la seconda è di mia proprietà (cioè ho bisogno di rilasciarla). C'è qualche ragione particolare per cui dovrei usare l'uno o l'altro, a prima vista sembra più facile usare il primo? Inoltre è il primo migliore in quanto dà al compilatore un senso di dimensioni?Definizione di NSMutableString?

NSMutableString *newPath = [NSMutableString stringWithCapacity:42]; 

O

NSMutableString *newPath = [[NSMutableString alloc] init]; 

EDIT ... ANCHE

Vedo un sacco a dichiarazioni scritte su due linee (cioè)

NSMutableString *newPath; 
newPath = [NSMutableString stringWithCapacity:42]; 

Personalmente preferisco il one Fodera, è solo un altro esempio di stile personale?

risposta

16
NSMutableString *newPath = [NSMutableString stringWithCapacity:42]; 

O

NSMutableString *newPath = [[NSMutableString alloc] init]; 

C'è un motivo particolare per cui dovrei usare uno o l'altro, sulla faccia di esso sembra più facile da usare al primo?

Sì. Autorizzi sempre immediatamente a meno che tu non abbia una ragione specifica per non farlo.

Il primo motivo è che è molto facile dimenticare di scrivere il messaggio release. Se autorizzi l'oggetto nella stessa dichiarazione in cui lo crei (come in [[[… alloc] init] autorelease]), è molto più difficile dimenticarlo e molto più ovvio quando lo fai. I metodi Factory Convenience (come stringWithCapacity:) autorizzano automaticamente l'oggetto per te, così come quando lo autorizzi tu stesso, non devi preoccuparti di rilasciarlo in seguito.

In secondo luogo, anche se ti ricordi di scrivere il messaggio separato release, è facile non toccarlo. Due modi sono i primi ritorni:

NSString *str = [[NSString alloc] initWithString:@"foo"]; 

BOOL success = [str writeToFile:path atomically:NO]; 
if (!success) 
    return; 

[str release]; 

e le eccezioni sollevate o propagate:

NSString *str = [[NSString alloc] initWithString:@"foo"]; 

//Throws NSRangeException if str is not in the array or is only in the array as the last object 
NSString *otherStr = [myArray objectAtIndex:[myArray indexOfObject:str] + 1]; 

[str release]; 

La “ragione specifica per non” è in generale che si ha un ciclo stretto che crea un sacco di oggetti, in cui caso potresti voler gestire manualmente quanti più oggetti nel loop puoi, al fine di mantenere il tuo conto alla rovescia. Tuttavia, esegui questa operazione solo se hai la prova che questo è il tuo problema (che si tratti di numeri rigidi da squalo, numeri rigidi da strumenti o il tuo sistema che va all'inferno di paging ogni volta che il ciclo viene eseguito abbastanza a lungo).

Altre soluzioni, possibilmente migliori, includono la suddivisione del loop in due loop nidificati (quello esterno per creare e drenare un pool di autorelease per il loop interno) e passare a NSOperation. (Tuttavia, assicurarsi di impostare un limite al numero di operazioni la coda viene eseguito in un momento, altrimenti, you may make it even easier to go into paging hell.)

è anche la prima meglio in quanto dà al compilatore un senso di grandezza?

È meglio, ma non per questo motivo.

Al compilatore, è solo un altro messaggio di classe. Il compilatore non sa o si cura di quello che fa; per tutto ciò che sa e si preoccupa, stringWithCapacity: è il messaggio per riprodurre un brano per l'utente.

Dà a NSMutableString un suggerimento per la dimensione: la classe saprà quanto spazio di memorizzazione dei caratteri potrebbe desiderare di allocare inizialmente. Qualunque sia il vantaggio che ottieni da questo è probabilmente piccolo (almeno sul Mac), ma se hai le informazioni a portata di mano, perché non usarlo? Al contrario, non andrei fuori dal mio modo di calcolarlo.

Vedo un sacco a dichiarazioni scritte su due linee (cioè)

NSMutableString *newPath; 
newPath = [NSMutableString stringWithCapacity:42]; 

Personalmente preferisco l'one-liner, è questo solo un altro esempio stile personale?

Sì. Tuttavia, vi è una certa quantità di rischio nel lasciare una variabile non inizializzata. Attivare definitivamente l'impostazione di generazione "Run Static Analyzer" se si decide di prendere l'abitudine.

+4

+1, tranne per il fatto che non sono d'accordo con "Avvia sempre automaticamente l'autorizzazione a meno che tu non abbia una ragione specifica per non farlo". La tua giustificazione ("è molto facile dimenticare di scrivere il messaggio" release ") non è così rilevante ora che l'analizzatore statico catturerà facilmente questo errore. Personalmente evito gli oggetti autoreleased, solo per evitare il cumulo di autorelease pool e il colpo che può venire dal drenare un pool di autorelease molto grande (che sì, ho notato). Generalmente preferisco ammortizzare il costo della deallocazione eseguendolo non appena ragionevolmente possibile. –

+0

L'analizzatore statico può impiegare un po 'di tempo per funzionare su qualche codice sorgente, quindi potresti non volerlo per tutto il tempo. E la maggior parte delle spese di drenaggio di un pool di autorelease è nel rilascio degli oggetti, che devi comunque fare. L'unica ragione che spicca è che li rilasci tutti (o alcuni di loro) in una sola volta invece di pochi millisecondi. –

+0

NON rilasciare o autorelease fino alla fine del metodo. Qualsiasi messaggio successivo all'autorelease può potenzialmente svuotare il pool di autorelease. Se stai restituendo un oggetto autoreleased, non vuoi alcuna possibilità che venga distrutto prima che il chiamante lo ottenga. – NSResponder

4

Il primo non è necessariamente per il compilatore, ma piuttosto un suggerimento alla stringa su come potrebbe essere in grado di ottimizzare la memorizzazione dei suoi dati. Questo è più utile per NSDictionary/NSArray/NSSet, che è have the ability to internally change their implementations depending on the size of their data set.

A parte questo, hai ragione: l'unica differenza è una questione di proprietà. Non utilizzo quasi mai i metodi WithCapacity e preferisco usare solo [NSMutableString string] o [NSMutableArray array], ma IMO, è davvero solo una questione di stile e non si guadagna o si perde nulla usando l'uno sull'altro.

+0

Quindi, personalmente, andresti a prendere l'allocazione e faresti il ​​rilascio da solo? – fuzzygoat

+0

@fuzzygoat - Dipende. Potrei usare l'uno o l'altro in base ai vincoli della memoria, sia che scriva codice tergente, ecc. –

1

Hai sollevato domande valide. Dipende davvero da quello che stai facendo, ma per le applicazioni generali di iPhone direi semplicemente di usare il primo. Questo verrà automaticamente ripulito per te quando il conteggio dei riferimenti arriva a 0 e non devi preoccuparti di questo.

Utilizzare il secondo quando si ha davvero una buona ragione per gestire da soli la memoria della stringa. Ad esempio, se si desidera essere sicuri di quando la stringa deve essere ripulita, o se si prevede che la memoria sia minima in un determinato momento.

Direi come regola generale utilizzare il secondo quando si ha una buona ragione per farlo.

2

Il primo è una stringa autorizzata. Questo sarà rilasciato dal sistema in un punto appropriato. Viene aggiunto al pool autorelease e la memoria verrà gestita dal sistema. Una volta fuori dal campo di applicazione, non è possibile garantire che sarà valido. Questo tipo è utile se ha solo scopo all'interno del tuo metodo e anche per restituire valori dai metodi.

Il secondo viene mantenuto, quindi avrà un conteggio di riferimento pari a 1 e non verrà aggiunto al pool di autorelease. Sei responsabile del rilascio e della liberazione della memoria. Utilizzare questo metodo se si desidera controllare l'ambito dell'oggetto. Utilizzato per variabili membro ecc.

Credo che l'inizializzazione a 2 righe sia solo stile, ma non utilizzerei la variazione a 2 linee poiché si sta definendo la variabile senza assegnare un valore ad esso, anche se si è sulla riga successiva . Credo che questo tipo di dichiarazione/inizializzazione delle variabili membro dei mirror, ma personalmente non mi piace molto.

1

Hai ragione su tutti i tuoi punti!

Non sono sicuro di quanto grande sia il suggerimento di dimensione/capacità, ma maggiori informazioni dovrebbero certamente consentire al tempo di esecuzione di prendere decisioni migliori.

Perché utilizzare uno stile rispetto all'altro?Bene, quando vengono rilasciati gli oggetti autorizzati? Ci sono due ragioni non ovvie per le quali potrebbe essere importante. Innanzitutto, quando un metodo utilizza molta memoria che è possibile rilasciare immediatamente. (Potresti anche usare un pool di autorelease locale, suppongo). In secondo luogo, trovo che usare autorelease può nascondere perdite di memoria e rendere più difficile il debug di alcuni codici. Il tuo chilometraggio può variare a seconda dell'età e della qualità del codice.

Quando ho iniziato a sviluppare app per iPhone ho utilizzato sempre oggetti autoreleased. Era comodo perché non capivo appieno come funzionava tutto e di solito faceva la cosa giusta. In questi giorni tendo a sbagliare sul lato della deallocazione manuale della memoria. In realtà non è così difficile capire come funziona il conteggio dei riferimenti e forzare immediatamente il problema quando non lo fai.

+0

"In secondo luogo, trovo che usare l'autorelease può nascondere perdite di memoria e rendere più difficile il debug di alcuni codici." Trovo il contrario. Se non rilasci qualcosa immediatamente (come in [[allocazione] init] autorelease]), è ** molto ** facile dimenticare il messaggio 'release' più tardi. –

+0

Inoltre, anche se ti ricordi di scrivere il messaggio 'release', è facile non colpirlo. Due modi sono i ritorni anticipati ('if (! Successo) return NO;') e le eccezioni generate o propagate. –

+0

L'ho deliberatamente definito "trovo", poiché apprezzo che si tratta di una preferenza personale. Faccio meno errori in questo modo in quanto mi costringe a considerare il ciclo di vita di una variabile quando scrivo il codice. Il tuo chilometraggio, come ho detto, può variare. –

0

Se si è abbastanza sicuri di quanto a lungo occorrerà una stringa, andare avanti e utilizzare il metodo -initWithCapacity :. Quando si supera la memoria di una stringa, questa viene riallocata e copiata, operazione non a buon mercato.