2009-05-24 7 views
44

Questo concetto sembra preoccuparmi. Perché un oggetto NSError ha bisogno che il suo puntatore passi a un metodo che sta modificando l'oggetto? Ad esempio, non basta passare un riferimento all'errore fare la stessa cosa?Perché NSError ha bisogno di una doppia indirezione? (puntatore a un puntatore)

NSError *anError; 
[myObjc doStuff:withAnotherObj error:error]; 

e poi in doStuff:

- (void)doStuff:(id)withAnotherObjc error:(NSError *)error 
{ 
    // something went bad! 
    [error doSomethingToTheObject]; 
} 

Perché non il lavoro di cui sopra, come la maggior parte degli altri modelli di messaggistica oggetto di lavoro? Perché invece dovremmo usare l'errore: (NSError **) errore?

risposta

75

Il modello NSError** viene utilizzato quando un metodo restituisce normalmente un valore, ma potrebbe essere necessario restituire un oggetto di errore (di tipo NSError*) in caso di errore. In Objective-C un metodo può restituire solo un tipo di oggetto, ma questo è il caso in cui si desidera restituire due. Nei linguaggi C-like quando è necessario restituire un valore extra chiedi un puntatore a un valore di quel tipo, quindi per restituire un NSError* è necessario un parametro NSError**. Un esempio più realistico sarebbe questo:

// The method should return something, because otherwise it could just return 
// NSError* directly and the error argument wouldn't be necessary 
- (NSArray *)doStuffWithObject:(id)obj error:(NSError **)error 
{ 
    NSArray *result = ...; // Do some work that might fail 
    if (result != nil) { 
    return result; 
    } else { 
    // Something went bad! 
    // The caller might pass NULL for `error` if they don't care about 
    // the result, so check for NULL before dereferencing it 
    if (error != NULL) { 
     *error = [NSError errorWithDomain:...]; 
    } 
    return nil; // The caller knows to check error if I return nil 
    } 
} 

Se solo si avesse un NSError* parametro invece di un NSError** poi doStuff sarebbe mai stato in grado di passare l'oggetto errore di tornare al chiamante.

6

dichiarazione alternativo di ciò che n8gray detto:

perché non stai ricevendo un oggetto a cui inviare messaggi; stai creando l'oggetto e restituendolo. Generalmente è necessario il parametro pointer-to-an- NSError *, poiché è possibile utilizzare l'istruzione return su una cosa alla volta e la si sta già utilizzando con NO.

+0

Ciao Peter :-) Spero che stiate iniziando il nuovo anno. Mentre sfioriamo "C" su "**" sono atterrato qui su questo post. Scusami, non riesco ancora a capirlo: se abbiamo passato solo un '* (un puntatore a un oggetto), questo dovrebbe essere sufficiente per apportare modifiche all'oggetto, giusto? Perché dovremmo ancora passare un puntatore a un puntatore a un oggetto per apportare modifiche a un oggetto? Grazie mille in anticipo. Saluti. – Unheilig

+3

@Unheilig: "se passassimo solo un' * '(un puntatore a un oggetto), ciò dovrebbe essere sufficiente per apportare modifiche all'oggetto, giusto?" Giusto. Hai solo bisogno del puntatore all'oggetto per inviare messaggi a quell'oggetto. "Perché dovremmo ancora passare un puntatore a un puntatore a un oggetto per apportare modifiche a un oggetto?" Perché non stai apportando modifiche a un oggetto; stai creando un nuovo oggetto e restituendolo al chiamante. Lo fai assegnandolo all'indirizzo che ti ha dato il chiamante, il puntatore alla variabile in cui metti il ​​puntatore all'oggetto. –

+0

+1. Grazie per aver chiarito tutto. – Unheilig

89

Molto semplicemente:

se si passa un puntatore ad un oggetto per la funzione, la funzione può modificare solo ciò che il puntatore sta puntando.

se si passa un puntatore a un puntatore a un oggetto, la funzione può modificare il puntatore in modo che punti a un altro oggetto.

Nel caso di NSError, la funzione potrebbe voler creare un nuovo oggetto NSError e passarvi un puntatore a quell'oggetto NSError. Pertanto, è necessario il doppio indirezione in modo che il puntatore possa essere modificato.

+7

Questo mi aiuta davvero a capire. Sono abituato a pensare ai puntatori nel contesto delle lingue che distinguono tra argomenti "byref" e "byval". Gli argomenti di Byref (cioè le variabili referenziate dai puntatori) possono essere modificati dalla funzione chiamata e il chiamante può vedere il cambiamento, quindi non ho capito la risposta di @ n8gray: "se tu avessi solo un' NSError * '...' doStuff' non sarebbe mai in grado di restituire l'oggetto errore al suo chiamante ". La tua risposta "la funzione può modificare il puntatore per puntare a un altro oggetto" rende chiaro PERCHÉ vorresti il ​​'**' se il chiamante può già vedere le modifiche con un semplice puntatore. – stifin

6

una vecchia questione, ma ancora penso che ne vale la pena mettere questo qui -

Il colpevole reale è NSError. Se si guarda al suo riferimento di classe, non ci sono metodi setter per nessuno dei suoi attributi, cioè dominio, codice o userInfo. Quindi non c'è modo, è sufficiente allocare e inizializzare un NSError, passarlo al metodo e quindi popolare le informazioni sull'oggetto NSError passato. (Se ci fosse stato un metodo setter, avremmo potuto passare un NSError * e fare qualcosa come error.code = 1 nel metodo.)

Quindi nel caso ci sia un errore, devi generare un nuovo oggetto NSError nel metodo e se lo fai, l'unico modo per passarlo al chiamante è avere un argomento NSError **. (Per la ragione spiegata nelle risposte precedenti.)

0

Non ho ancora ottenuto l'immagine completa leggendo tutte le risposte sopra. L'esercizio profano che ho fatto qui sotto, finalmente mi ha aiutato a capire cosa sta succedendo.Basta metterlo là fuori nel caso in cui aiuta altri principianti.

Supponete di avere seguito

@interface Class X 
-(void) methodX:(NSMutableArray *)array; 
@end 

in qualche altra parte del codice si ha la seguente sequenza

ClassX *objectX = [[ClassX alloc] init]; 
NSMutableArray *arrayXX = [@[@(1), @(2)] mutableCopy]; 
//What is stored in arrayXX is the address in the heap at which the NSMutableArray object starts, lets call this address ZZZ 
//array starting at address ZZZ in the heap now contains NSNUmbers @1,@2 
[objectX methodX:array] 

Quando si richiama [objectX methodX:array], ciò che viene ricevuto dal metodo è una copia di array. Poiché la matrice contiene un indirizzo (cioè un puntatore), la copia è speciale in quanto ciò che viene ricevuto è un'altra variabile con indirizzo ZZZ in essa.

Quindi, se methodX fa [array removeObjectAtIndex:0], l'oggetto che inizia all'indirizzo ZZZ viene interessato (ora contiene solo un NSNUmber @ (2)). Quindi, quando il metodo ritorna, anche l'array originale viene influenzato.

Supponiamo invece che methodX sia array = [@[@(2)] mutableCopy]; quindi l'array originale non viene interessato. Questo perché non hai inserito l'indirizzo ZZZ e cambiato qualcosa. Invece hai sovrascritto lo ZZZ nella copia ricevuta con il metodo su un diverso indirizzo YYY. L'indirizzo YYY è l'inizio di un oggetto NSMUtableArray con un elemento NSNUmber @ (2). L'indirizzo ZZZ originale contiene ancora un NSMUtableArray con due elementi. @ (1) e @ (2). Quindi, quando il metodo ritorna, l'array originale non viene modificato.

Problemi correlati