5

Sotto ARC, un out-parametro assume la seguente forma (per impostazione predefinita, questo è equivalente a NSError **):Perché l'indirizzo di un ivar non può essere passato a un argomento "id __autoreleasing *" sotto ARC?

- (BOOL)tryWithError:(NSError *__autoreleasing *)err; 

Dal Transitioning to ARC Release Notes, se si passa l'indirizzo di una variabile locale __strong, il compilatore creerà una variabile temporanea e generano il seguente codice:

NSError *error; // strong 
BOOL ok = [myObject tryWithError:&error]; 

// translated to 

NSError *__strong error; 
NSError *__autoreleasing tmp = error; 
BOOL ok = [myObject tryWithError:&tmp]; 
error = tmp; 

Ma se lo facciamo con una variabile di istanza:

@implementation Foo { 
    NSError *_error; // strong 
} 
- (void)bar 
{ 
    [myObject tryWithError:&_error]; 
} 
... 

questo ci dà l'errore

Passando indirizzo dell'oggetto non locale per __autoreleasing parametri per write-back.

Perché è valido? Non è possibile che il compilatore traduca automaticamente tale codice in questo modo?

- (void)bar 
{ 
    NSError *__autoreleasing tmp = _error; 
    [myObject tryWithError:&tmp]; 
    _error = tmp; 
} 

Dopo tutto, questo è quello che scriverò comunque per risolvere il problema!

Nota: aggiungendo la parola chiave out al tipo di parametro will reduce the compiler's work slightly perché non ha bisogno di leggere il valore corrente nella variabile temporanea - ma questo non prendersi cura dell'errore.

risposta

1

Un puntatore a un ivar non può essere passato a un argomento "id __autoreleasing *" sotto ARC perché questo tipo di pass-by-writeback è mal formato. Le forme respective section in the ARC specification elenchi legali di pass-by-writeback, l'unica applicabile ecco

& var, dove var è una variabile scalare di durata memorizzazione automatica con l'oggetto a conservazione

, soltanto è consentita la durata della memorizzazione automatica (una variabile locale).

Perchè questo non è valido: Sono abbastanza sicuro che il motivo per qui è la compatibilità con il vecchio codice:

1) You should only look at the error writeback in the failure case. Nel caso di successo, non vi è alcuna garanzia di cosa si trova all'interno del puntatore di errore.

2) In generale, se il valore di writeback deve essere utilizzato o meno dipende dal contratto del metodo. Questo è qualcosa che il compilatore non può controllare.

Questa è la versione del codice che corrisponde al tipo di &error (NSError * __autoreleasing *) al tipo della ripresa (NSError ** che viene interpretato come NSError * __autoreleasing *). Se ok è SÌ, il valore dell'errore non verrà toccato.

NSError * __autoreleasing error; 
BOOL OK = [myObject performOperationWithError:&error]; 
if (!OK) { 
    // use error 
} 

Tuttavia, coloro __autoreleasing sono brutti, così invece di costringerci a usare __autoreleasing tutto il luogo, il compilatore ci permette di passare una variabile __strong (ma locale), nonché (proprietà di default):

NSError *error; 
BOOL OK = [myObject performOperationWithError:&error]; 
if (!OK) { 
    // use error 
} 

Secondo la documentazione, che ottiene riscritti per:

NSError * __strong error; 
NSError * __autoreleasing tmp = error; 
BOOL OK = [myObject performOperationWithError:&tmp]; 
error = tmp; 
if (!OK) { 
    // use error 
} 

Non è un problema a tutti, saranno utilizzati solo l'errore di i n il caso di successo.

Ora diamo un'occhiata a una variabile di istanza __strong_error. Perché il compilatore non lo consente? Ecco cosa la riscrittura sarà simile:

NSError * __autoreleasing tmp = _error; 
BOOL OK = [myObject performOperationWithError:&tmp]; 
_error = tmp; 
if (!OK) { 
    // use error 
} 

Il problema qui è che la ripresa di valore in tmp sarebbe sempre essere utilizzato (assegnato all'istanza variabili _error), ignorando il contratto del metodo che la il writeback dovrebbe essere usato solo in casi di errore (o in generale qualunque sia la documentazione del metodo). Un modo sicuro per assegnare l'ultimo errore di una variabile di istanza sarebbe

NSError * __autoreleasing tmp = _error; 
BOOL OK = [myObject performOperationWithError:&tmp]; 
if (!OK) { 
    _error = tmp; 
    // use error 
} else { 
    _error = nil; // Make sure that _error is nil if there was no error. 
} 

E questo è vero solo per la convenzione di metodi di cacao che restituiscono un errore. In generale, non c'è modo per il compilatore di dire cosa farà un metodo con un id *: potrebbero esserci vecchi metodi che usano convenzioni diverse. Quindi, se vuoi davvero archiviare il writeback in una variabile di istanza __strong, attualmente devi fare il miglio supplementare da solo, e non mi aspetto che questo cambi.

+0

"Non puoi farlo perché è vietato" non spiega molto del perché:/ – Norswap

+0

@Norswap Ho aggiunto un po 'più di informazioni su questo, spero che ti aiuti meglio. –

+0

Grazie mille! Ma quello che non capisco è questo: se va bene passare una variabile locale forte ad un parametro '* __autoreleasing *', perché non è accettabile passare una variabile di istanza forte? Il documento "Transizione verso note di rilascio ARC" (nella sezione "Qualificatori di variabili") spiega cosa succede quando si passa una variabile locale forte. Non capisco perché la stessa logica non possa essere applicata a variabili di istanza forti. – Norswap

Problemi correlati