Зачем NSError нужна двойная косвенность? (указатель на указатель)
Эта концепция, кажется, беспокоит меня. Почему объекту NSError нужно, чтобы его указатель был передан методу, который модифицирует объект? Например, не передаст ли ссылка на ошибку то же самое?
NSError *anError;
[myObjc doStuff:withAnotherObj error:error];
а затем в doStuff:
- (void)doStuff:(id)withAnotherObjc error:(NSError *)error
{
// something went bad!
[error doSomethingToTheObject];
}
Почему вышеперечисленное не работает так, как работает большинство других шаблонов обмена объектными сообщениями? Почему вместо этого мы должны использовать error:(NSError **)error?
5 ответов
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
никогда не сможет передать объект ошибки обратно вызывающей стороне.
Довольно просто:
если вы передадите указатель на объект в вашу функцию, функция может изменить только то, на что указывает указатель.
если вы передаете указатель на указатель на объект, то функция может изменить указатель так, чтобы он указывал на другой объект.
В случае NSError функция может захотеть создать новый объект NSError и передать вам указатель на этот объект NSError. Таким образом, вам нужно двойное косвенное обращение, чтобы указатель можно было изменить.
Старый вопрос, но все же я думаю, что стоит поставить это здесь -
Фактический виновник - NSError. Если вы посмотрите на его ссылку на класс, то нет никаких методов установки для любого из его атрибутов, то есть домена, кода или userInfo. Таким образом, нет никакого способа, вы можете просто выделить и инициализировать NSError, передать его методу и затем заполнить информацию о переданном объекте NSError. (Если бы был метод установки, мы могли бы просто передать NSError * и сделать что-то вроде error.code = 1 в методе.)
Таким образом, в случае возникновения ошибки вы должны сгенерировать новый объект NSError в методе, и если вы делаете это, единственный способ передать его обратно вызывающей стороне - получить аргумент NSError **. (По причине, изложенной в ответах выше.)
Альтернативное заявление о том, что n8gray сказал:
Потому что вы не получаете объект для отправки сообщений; вы создаете объект и возвращаете его. Как правило, вам нужен указатель на анNSError *
аргумент, потому что вы можете использовать только return
заявление по одному, и вы уже используете его с NO
,
Я все еще не получил полную картину, прочитав все ответы выше. Непрофессиональное упражнение, которое я выполнил ниже, наконец помогло мне понять, что происходит. Просто поместите это на тот случай, если это поможет другим новичкам.
Предположим, у вас есть следующие
@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)). Таким образом, когда метод возвращается, исходный массив также затрагивается.
Предположим, что вместо methodX array = [@[@(2)] mutableCopy];
тогда оригинальный массив не будет затронут. Это потому, что вы не пошли в адрес ZZZ и что-то изменили. Вместо этого вы перезаписали ZZZ в копии, полученной методом, на другой адрес YYY. Адрес YYY - это начало объекта NSMUtableArray с одним элементом NSNUmber @(2). Исходный адрес ZZZ по-прежнему содержит NSMUtableArray с двумя элементами. @(1) и @ (2). Таким образом, когда метод возвращается, исходный массив не изменяется.