Подтвердить что ты не робот

Почему NSError нуждается в двойной косвенности? (указатель на указатель)

Эта концепция меня беспокоит. Почему объект NSError нуждается в указателе, переданном методу, который модифицирует объект? Например, не просто ли передать ссылку на ошибку, сделайте то же самое?

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

а затем в doStuff:

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

Почему это не работает, как большинство других шаблонов обмена объектами? Почему вместо этого мы должны использовать ошибку: (NSError **)?

4b9b3361

Ответ 1

Шаблон NSError** используется, когда метод обычно возвращает какое-то значение, но вместо этого может потребоваться вернуть объект ошибки (типа NSError*), если он терпит неудачу. В Objective-C метод может возвращать только один тип объекта, но это случай, когда вы хотите вернуть два. В C-подобных языках, когда вам нужно вернуть дополнительное значение, вы запрашиваете указатель на значение этого типа, поэтому для возврата NSError* вам нужен параметр NSError**. Более реалистичным примером может быть следующее:

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

Если у вас был только NSError* параметр вместо NSError**, то doStuff никогда не сможет передать объект ошибки обратно своему вызывающему.

Ответ 2

Довольно просто:

если вы передаете указатель на объект вашей функции, функция может изменять только то, на что указывает указатель.

если вы передаете указатель на указатель на объект, тогда функция может изменить указатель, чтобы указать на другой объект.

В случае NSError функция может захотеть создать новый объект NSError и передать вам указатель на этот объект NSError. Таким образом, вам нужна двойная косвенность, чтобы можно было изменить указатель.

Ответ 3

Альтернативный вывод того, что сказал n8gray:

Потому что вы не получаете объект для отправки сообщений; вы создаете объект и возвращаете его. Обычно вам нужен аргумент pointer-to-an- NSError * -variable, потому что вы можете использовать оператор return только один раз за раз, и вы уже используете его с NO.

Ответ 4

Старый вопрос, но все же я думаю, что его стоит положить здесь -

Фактический виновник NSError. Если вы посмотрите на его ссылку на класс, для любого из его атрибутов, например домена, кода или пользовательской информации, нет методов настройки. Таким образом, нет способа, вы можете просто назначить и инициализировать NSError, передать его методу и затем заполнить информацию об переданном объекте NSError. (Если бы был метод setter, мы могли бы просто передать NSError * и сделать что-то вроде error.code = 1 в методе.)

Итак, в случае возникновения ошибки вы должны сгенерировать новый новый объект NSError в методе, и если вы делаете это, единственный способ передать его обратно вызывающему абоненту - это аргумент NSError **. (По причинам, указанным в приведенных выше ответах.)

Ответ 5

Я все еще не получил полную картину, прочитав все ответы выше. Занятие непрофессионала, которое я делал ниже, помогло мне понять, что происходит. Просто поместите его там, если это поможет другим новичкам.

Предположим, что у вас есть следующие

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

В некоторой другой части кода у вас есть следующая последовательность

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]

Когда вы вызываете [objectX methodX:array], то, что получает метод, является копией array. Поскольку массив содержит адрес (т.е. Указатель), копия является особенной в том, что то, что получено, является другой переменной с адресом ZZZ в ней.

Итак, если methodX делает [array removeObjectAtIndex:0], то объект, начинающийся с адреса ZZZ, подвергается воздействию (теперь он содержит только один NSNUmber @(2)). Таким образом, когда метод возвращается, исходный массив также подвергается воздействию.

Предположим, что вместо метода X does array = [@[@(2)] mutableCopy]; исходный массив не будет затронут. Это потому, что вы не вошли в адрес ZZZ и что-то изменили. Вместо этого вы переписали ZZZ в копии, полученной методом, на другой адрес YYY. Адрес YYY - это начало объекта NSMUtableArray с одним элементом NSNUmber @(2). Исходный адрес ZZZ по-прежнему содержит NSMUtableArray с двумя элементами. @(1) и @(2). Таким образом, когда метод возвращается, исходный массив не изменяется.