In quali situazioni è necessario scrivere il qualificatore di proprietà __autoreleasing sotto ARC?


118

Sto cercando di completare il puzzle.

__strongè l'impostazione predefinita per tutti i puntatori a oggetti conservabili Objective-C come NSObject, NSString, ecc. È un riferimento forte. ARC lo bilancia con una -releasealla fine dell'oscilloscopio.

__unsafe_unretainedè uguale alla vecchia maniera. Viene utilizzato per un puntatore debole senza mantenere l'oggetto conservabile.

__weakè come __unsafe_unretainedtranne che è un riferimento debole con azzeramento automatico, il che significa che il puntatore verrà impostato su nil non appena l'oggetto di riferimento viene deallocato. Ciò elimina il pericolo di puntatori penzolanti ed errori EXC_BAD_ACCESS.

Ma a cosa serve esattamente __autoreleasing? Faccio fatica a trovare esempi pratici su quando devo usare questo qualificatore. Credo che sia solo per funzioni e metodi che si aspettano un puntatore puntatore come:

- (BOOL)save:(NSError**);

o

NSError *error = nil;
[database save:&error];

che sotto ARC deve essere dichiarato in questo modo:

- (BOOL)save:(NSError* __autoreleasing *);

Ma questo è troppo vago e mi piacerebbe capire appieno il motivo . I frammenti di codice che trovo collocano il rilascio __autorelease tra le due stelle, il che mi sembra strano. Il tipo è NSError**(un puntatore-puntatore a NSError), quindi perché posizionarlo __autoreleasingtra le stelle e non semplicemente davanti a NSError**?

Inoltre, potrebbero esserci altre situazioni in cui devo fare affidamento __autoreleasing.


1
Ho la stessa domanda e le risposte seguenti non sono del tutto convincenti ... ad esempio, perché il sistema non fornisce interfacce che accettano argomenti NSError ** dichiarati con il decoratore __autoreleasing come te e le note di rilascio Transitioning to Arc dicono che dovrebbe essere? ad esempio, una qualsiasi delle molte di queste routine in NSFileManager.h ??
Papà

Risposte:


67

Hai ragione. Come spiega la documentazione ufficiale:

__autoreleasing per denotare argomenti passati per riferimento (id *) e vengono rilasciati automaticamente al ritorno.

Tutto questo è spiegato molto bene nella guida alla transizione ARC .

Nel tuo esempio NSError, la dichiarazione significa __strong, implicitamente:

NSError * e = nil;

Sarà trasformato in:

NSError * __strong error = nil;

Quando chiami il tuo savemetodo:

- ( BOOL )save: ( NSError * __autoreleasing * );

Il compilatore dovrà quindi creare una variabile temporanea, impostata su __autoreleasing. Così:

NSError * error = nil;
[ database save: &error ];

Sarà trasformato in:

NSError * __strong error = nil;
NSError * __autoreleasing tmpError = error;
[ database save: &tmpError ];
error = tmpError;

Puoi evitarlo dichiarando l'oggetto errore come __autoreleasing, direttamente.


3
No, __autoreleasingviene utilizzato solo per argomenti passati per riferimento. Questo è un caso speciale, poiché hai un puntatore al puntatore di un oggetto. Questo non è il caso di cose come i costruttori di convenienza, poiché restituiscono solo un puntatore a un oggetto e poiché ARC lo gestisce automaticamente.
Macmade

7
Perché il qualificatore __autoreleasing è posto tra le stelle e non solo davanti a NSError **? Questo mi sembra strano in quanto il tipo è NSError **. O è perché questo sta cercando di indicare che il puntatore NSError * puntato deve essere qualificato come puntante a un oggetto rilasciato automaticamente?
Orgoglioso membro

1
@ Membro orgoglioso per quanto riguarda il tuo primo commento - non è corretto (se ti ho capito bene) - vedi la risposta di Glen Low di seguito. L'oggetto errore viene creato e assegnato a una variabile di rilascio automatico (quella che hai passato) all'interno della funzione di salvataggio. Questa assegnazione fa sì che l'oggetto venga conservato e rilasciato automaticamente in quel momento. La dichiarazione della funzione di salvataggio ci impedisce di inviarle qualcosa di diverso da una variabile di rilascio automatico perché è ciò di cui ha bisogno - motivo per cui il compilatore crea una variabile temporanea se ci proviamo.
Colin

2
Allora perché nessuna delle interfacce Apple sembra avere questo? ad esempio, tutto in NSFileManager.h?
Papà

1
@Macmade: solo per caso ho notato che la tua risposta è stata modificata (da stackoverflow.com/users/12652/comptrol ) e ho l'impressione che almeno le modifiche al tuo primo esempio ("implicitamente ... verranno trasformate in ...) sono sbagliati, perché la qualifica __ forte è stata spostata dalla seconda riga alla prima riga. Forse potresti controllare.
Martin R

34

Seguendo la risposta di Macmade e la domanda di follow-up di Proud Member nei commenti, (avrei anche pubblicato questo come commento ma supera il numero massimo di caratteri):

Ecco perché il qualificatore variabile di __autoreleasing è posto tra le due stelle.

Per prefigurare, la sintassi corretta per dichiarare un puntatore a un oggetto con un qualificatore è:

NSError * __qualifier someError;

Il compilatore lo perdonerà:

__qualifier NSError *someError;

ma non è corretto. Consulta la guida alla transizione di Apple ARC (leggi la sezione che inizia con "Dovresti decorare le variabili correttamente ...").

Per affrontare la domanda in questione: un doppio puntatore non può avere un qualificatore di gestione della memoria ARC perché un puntatore che punta a un indirizzo di memoria è un puntatore a un tipo primitivo, non un puntatore a un oggetto. Tuttavia, quando dichiari un doppio puntatore, ARC desidera sapere quali sono le regole di gestione della memoria per il secondo puntatore. Ecco perché le variabili del doppio puntatore sono specificate come:

SomeClass * __qualifier *someVariable;

Quindi, nel caso di un argomento del metodo che è un doppio puntatore NSError, il tipo di dati è dichiarato come:

- (BOOL)save:(NSError* __autoreleasing *)errorPointer;

che in inglese dice "pointer to an __autoreleasing NSError object pointer".


15

La specifica ARC definitiva lo dice

Per gli oggetti __autoreleasing, il nuovo puntatore viene mantenuto, rilasciato automaticamente e archiviato in lvalue utilizzando la semantica primitiva.

Quindi, ad esempio, il codice

NSError* __autoreleasing error = someError;

viene effettivamente convertito in

NSError* error = [[someError retain] autorelease];

... motivo per cui funziona quando hai un parametro NSError* __autoreleasing * errorPointer, il metodo chiamato assegnerà quindi l'errore *errorPointere la semantica di cui sopra entrerà in azione.

Potresti usarlo __autoreleasingin un contesto diverso per forzare un oggetto ARC nel pool di rilascio automatico, ma non è molto utile poiché ARC sembra utilizzare solo il pool di rilascio automatico al ritorno del metodo e lo gestisce già automaticamente.

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.