2009-05-24 22 views
44

Este concepto parece molestarme. ¿Por qué un objeto NSError necesita que su puntero pase a un método que está modificando el objeto? Por ejemplo, ¿no solo hacer una referencia al error haría lo mismo?¿Por qué NSError necesita doble indirección? (puntero a un puntero)

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

y luego en hacerTarea:

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

¿Por qué no el trabajo anterior como la mayoría de otros patrones de mensajería objeto de trabajo? ¿Por qué, en su lugar, debemos utilizar el error: error (NSError **)?

Respuesta

75

El patrón NSError** se usa cuando un método normalmente devuelve algún valor, pero en su lugar puede necesitar devolver un objeto de error (del tipo NSError*) si falla. En Objective-C, un método solo puede devolver un tipo de objeto, pero este es un caso en el que desea devolver dos. En los idiomas similares a C cuando necesita devolver un valor extra, solicita un puntero a un valor de ese tipo, por lo que para devolver un NSError* necesita un parámetro NSError**. Un ejemplo más realista sería la siguiente:

// 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 
    } 
} 

Si sólo tuviera un parámetro en lugar de un NSError**NSError* continuación doStuff nunca sería capaz de pasar el objeto de error a su llamador.

6

declaración alternativo de lo n8gray dijo:

porque no estás recibiendo un objeto para enviar mensajes a; estás creando el objeto y regresándolo. Por lo general, necesita el argumento variable-puntero-a-un NSError * porque solo puede usar la declaración return en una cosa a la vez, y ya la está usando con NO.

+0

Hola, Peter :-) Espero que estés teniendo un buen comienzo en el nuevo año. Mientras revisaba 'C' sobre' ** ', llegué aquí en esta publicación. Disculpe, todavía no lo entiendo: si pasamos solo un '*' (un puntero a un objeto), eso sería suficiente para hacer cambios en el objeto, ¿verdad? ¿Por qué tendríamos que pasar un puntero a un puntero a un objeto para hacer cambios en un objeto? Muchas gracias de antemano. Saludos. – Unheilig

+3

@Unheilig: "si pasamos solo un' * '(un puntero a un objeto), eso sería suficiente para hacer cambios en el objeto, ¿verdad?" A la derecha. Solo necesita el puntero al objeto para enviar mensajes a ese objeto. "¿Por qué todavía tendríamos que pasar un puntero a un puntero a un objeto para hacer cambios en un objeto?" Porque no estás haciendo cambios en un objeto; estás creando un nuevo objeto y devolviéndolo a la persona que llama. Lo hace asignándolo a la dirección que le dio la persona que llama: el puntero a la variable donde colocará el puntero al objeto. –

+0

+1. Gracias por aclarar esto también. – Unheilig

89

En pocas palabras:

si pasa un puntero a un objeto a su función, la función sólo se puede modificar lo que el puntero está apuntando.

si pasa un puntero a un puntero a un objeto, entonces la función puede modificar el puntero para apuntar a otro objeto.

En el caso de NSError, la función puede querer crear un nuevo objeto NSError y devolverle un puntero a ese objeto NSError. Por lo tanto, necesita doble direccionamiento indirecto para que el puntero se pueda modificar.

+7

Esto realmente me ayuda a entender. Estoy acostumbrado a pensar en punteros en el contexto de los idiomas que distinguen entre argumentos "byref" y "byval". Los byref args (es decir, las variables a las que hacen referencia los punteros) pueden ser cambiados por la función llamada y la persona que llama puede ver el cambio, así que no entendí la respuesta de @n8gray: "si solo tuvieras un' NSError * '...' doStuff' nunca podría pasar el objeto de error a su llamador ". Su respuesta "la función puede modificar el puntero para apuntar a otro objeto" deja en claro POR QUÉ usted querría el '**' si la persona que llama ya puede ver los cambios con solo un puntero. – stifin

6

Una cuestión de edad, pero aún así creo que es digno de poner esto aquí -

El verdadero culpable es NSError. Si observa su referencia de clase, no hay métodos de establecimiento para ninguno de sus atributos, es decir, dominio, código o userInfo. De modo que no hay forma de hacerlo, solo puede asignar e inicializar un NSError, pasarlo al método y luego completar la información en el objeto NSError pasado. (Si hubiera habido un método setter, podríamos haber pasado un NSError * y hecho algo como error.code = 1 en el método.)

Entonces, en caso de que haya un error, debe generar un nuevo objeto NSError en el método y si lo hace, la única manera de devolverlo a la persona que llama es tener un argumento NSError **. (Por la razón explicada en las respuestas anteriores.)

0

Todavía no entiendo la imagen completa leyendo todas las respuestas anteriores. El ejercicio de lego que hice a continuación, finalmente me ayudó a entender lo que está sucediendo.Solo ponerlo ahí en caso de que ayude a otros principiantes.

Suponga que tiene tras

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

En alguna otra parte del código que tiene la siguiente secuencia

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] 

Cuando se invoca [objectX methodX:array], lo que está siendo recibida por el método es una copia de array. Como la matriz contiene una dirección (es decir, un puntero), la copia es especial porque lo que se recibe es otra variable con la dirección ZZZ en ella.

Entonces, si el métodoX hace [array removeObjectAtIndex:0], entonces el objeto que comienza en la dirección ZZZ se ve afectado (ahora solo contiene un NSNUmber @ (2)). Entonces, cuando el método retorna, la matriz original también se ve afectada.

Supongamos que en su lugar el métodoX hace array = [@[@(2)] mutableCopy]; y luego la matriz original no se ve afectada. Esto es porque no entraste en la dirección ZZZ y cambiaste algo. En su lugar, sobrescribió el ZZZ en la copia recibida por el método en una dirección diferente YYY. La dirección YYY es el inicio de un objeto NSMUtableArray con un elemento NSNUmber @ (2). La dirección ZZZ original todavía contiene un NSMUtableArray con dos elementos. @ (1) y @ (2). Entonces, cuando el método retorna, la matriz original no se ve afectada.

Cuestiones relacionadas