que - ¿Por qué NSError necesita doble indirección?(puntero a un puntero)
punteros como parametros de funciones en c (5)
Declaración alternativa de lo que n8gray dijo:
Porque no recibes un objeto para enviar mensajes; estás creando el objeto y regresándolo. Por lo general, necesita el argumento de la NSError *
puntero a NSError *
porque solo puede usar la declaración return
en una cosa a la vez, y ya la está usando con NO
.
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?
NSError *anError;
[myObjc doStuff:withAnotherObj error:error];
y luego en doStuff:
- (void)doStuff:(id)withAnotherObjc error:(NSError *)error
{
// something went bad!
[error doSomethingToTheObject];
}
¿Por qué no funciona lo anterior como la mayoría de los otros patrones de mensajería de objetos? ¿Por qué, en su lugar, debemos utilizar el error: error (NSError **)?
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 (de 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 este:
// 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 solo tiene un parámetro NSError*
lugar de un NSError**
, doStuff
nunca podrá pasar el objeto de error a su llamador.
Muy simple:
si pasa un puntero a un objeto para su función, la función solo puede modificar a lo que apunta el puntero.
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.
Todavía no obtuve la imagen completa al leer 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.
Supongamos que tiene siguiente
@interface Class X
-(void) methodX:(NSMutableArray *)array;
@end
En alguna otra parte del código 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 invoca [objectX methodX:array]
, lo que el método está recibiendo es una copia de la array
. Como la matriz contiene una dirección (es decir, es un puntero), la copia es especial porque lo que se recibe es otra variable con la dirección ZZZ en ella.
Entonces, si methodX 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 methodX hace array = [@[@(2)] mutableCopy];
entonces 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 a 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.
Una vieja pregunta, pero aún así creo que vale la pena 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 forma de devolverlo al llamador es tener un argumento NSError **. (Por la razón explicada en las respuestas anteriores).