Dlaczego NSError wymaga podwójnego podziału? (wskaźnik do wskaźnika)

Ta koncepcja wydaje mi się kłopotliwa. Dlaczego obiekt NSError wymaga przekazania wskaźnika do metody modyfikującej obiekt? Na przykład, czy samo podanie odniesienia do błędu nie zrobi tego samego?

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

A następnie w doStuff:

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

Dlaczego powyższe nie działa jak większość innych wzorców wiadomości obiektowych? Dlaczego zamiast tego musimy użyć error: (nserror **)error?

Author: Coocoo4Cocoa, 2009-05-24

5 answers

Wzorzec NSError** jest używany, gdy metoda normalnie zwraca jakąś wartość, ale zamiast tego może być konieczne zwrócenie obiektu błędu (typu NSError*), jeśli się nie powiedzie. W Objective-C metoda może zwrócić tylko jeden typ obiektu, ale jest to przypadek, w którym chcesz zwrócić dwa. W językach podobnych do C, gdy musisz zwrócić dodatkową wartość, prosisz o wskaźnik do wartości tego typu, więc aby zwrócić NSError*, potrzebujesz parametru NSError**. Bardziej realistycznym przykładem może być:

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

Gdybyś miał tylko NSError* parametr zamiast NSError** wtedy doStuff nigdy nie będzie w stanie przekazać obiektu błędu z powrotem do jego wywołującego.

 79
Author: n8gray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-28 19:08:57

Po prostu:

Jeśli przekazujesz wskaźnik do obiektu do funkcji, funkcja może zmodyfikować tylko to, na co wskazuje wskaźnik.

Jeśli przekazujesz wskaźnik do wskaźnika do obiektu, funkcja może zmodyfikować wskaźnik, aby wskazywał na inny obiekt.

W przypadku NSError funkcja może chcieć utworzyć nowy obiekt NSError i przekazać wskaźnik do tego obiektu nserror. W związku z tym konieczne jest podwójne przekierowanie, aby można było modyfikować wskaźnik.

 96
Author: rein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-24 06:02:09

Stare pytanie, ale i tak myślę, że warto to tutaj umieścić-

Faktycznym winowajcą jest NSError . Jeśli spojrzysz na jego odniesienie do klasy, nie ma metod settera dla żadnego z jego atrybutów, tj. domeny, kodu lub userInfo. Tak więc nie ma możliwości, możesz po prostu alloc i zainicjować nserror, przekazać go do metody, a następnie wypełnić informacje o przekazanym obiekcie NSError. (Gdyby istniała metoda setter, moglibyśmy po prostu przekazać NSError * i zrobić coś w rodzaju błędu.kod = 1 w metodzie.)

Więc w przypadku wystąpienia błędu, musisz wygenerować nowy obiekt NSError w metodzie i jeśli to robisz, jedynym sposobem przekazania go z powrotem do wywołującego jest posiadanie argumentu NSError **. (Z powodu wyjaĹ " nionego w powyĺľszych odpowiedziach.)

 10
Author: OutOnAWeekend,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-28 06:16:13

Alternatywne stwierdzenie tego, co powiedział n8gray:

Ponieważ nie odbierasz obiektu do wysyłania wiadomości; tworzysz obiekt i zwracasz go. Zazwyczaj potrzebujesz argumentu pointer-to-an - NSError * - variable, ponieważ możesz używać tylko Instrukcji return na jednej rzeczy naraz, a już używasz jej z NO.

 7
Author: Peter Hosey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-24 05:52:28

Nadal nie uzyskałem pełnego obrazu, czytając wszystkie odpowiedzi powyżej. Ćwiczenie laika, które zrobiłem poniżej, w końcu pomogło mi zrozumieć, co się dzieje. Po prostu umieszczam go tam na wypadek, gdyby pomógł innym początkującym.

Załóżmy, że masz następujące

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

W innej części kodu masz następującą sekwencję

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]

Kiedy wywołujesz [objectX methodX:array], to co jest odbierane przez metodę jest kopią z array. Ponieważ tablica zawiera adres (czyli jest pointer), Kopia {[14] } jest szczególna w tym, że otrzymywana jest inna zmienna o adresie ZZZ.

Tak więc, jeśli methodX robi [array removeObjectAtIndex:0], to obiekt zaczynający się od adresu ZZZ zostanie dotknięty (teraz zawiera tylko jeden NSNUmber @(2)). Tak więc, gdy metoda powróci, oryginalna tablica również zostanie naruszona.

Załóżmy, że zamiast metody methodX array = [@[@(2)] mutableCopy];, oryginalna tablica nie zostanie naruszona. Dzieje się tak dlatego, że nie wszedłeś w adres ZZZ i coś zmieniłeś. Zamiast tego przerobiłeś ZZZ w kopii otrzymanej metodą na inny adres YYY. Adres YYY jest początkiem obiektu NSMUtableArray z jednym elementem nsnumber @(2). Oryginalny adres ZZZ nadal zawiera NSMUtableArray z dwoma elementami. @(1) i @(2). Tak więc, gdy metoda powróci, oryginalna tablica jest nienaruszona.

 0
Author: Smart Home,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-12 06:40:55