Почему здесь используется "error:&error" (target-c)
Почему здесь используется "error:&error" (t arget-c)
NSError *error = nil;
NSArray *array = [moc executeFetchRequest:request error:&error];
разве объект в t arget-c не будет эффективно передаваться по ссылке?
2 ответа
Тип аргумента для error:
является NSError**
(т.е. указатель на указатель на объект). Это позволяет moc
объект для выделения и инициализации нового NSError
объект по мере необходимости. Это обычная картина, особенно в какао.
Документация NSError дает некоторое представление о мотивации этого подхода:
Приложения могут выбрать создание подклассов NSError, чтобы обеспечить более локализованные строки ошибок путем переопределения localizedDescription.
Проходя в NSError**
аргумент позволяет этому методу возвращать любой подкласс NSError
в этом есть смысл. Если вы прошли в NSError*
, вам придется поставить существующий NSError
объект, и у метода не будет способа вернуть объект, отличный от того, который вы передали.
Чтобы было понятно, метод может выглядеть примерно так:
- (NSArray*)executeFetchRequest:(Request *)request error:(NSError**)error {
...
if ((error != NULL) && (some_error_condition)) {
*error = [[[SomeNSErrorSubclass alloc] init...] autorelease];
return nil;
}
}
Обратите внимание, что это также позволяет вызывающему коду игнорировать ошибки, просто передавая NULL
для error:
параметр, следующим образом:
NSArray *array = [moc executeFetchRequest:request error:NULL];
Обновление: (в ответ на вопросы):
Есть две причины, почему тип аргумента должен быть NSError**
вместо NSError*
: 1. правила переменной области видимости и 2. экземпляры NSError являются неизменяемыми.
Причина № 1: переменные правила области видимости
Давайте предположим, что объявление функции должно было выглядеть так:
- (NSArray*)executeFetchRequest:(Request *)request error:(NSError*)error;
И мы должны были вызвать функцию следующим образом:
NSError * error = nil;
[someArray executeFetchRequest:someRequest error:error];
if (error != nil) { /* handle error */ }
Когда вы передаете переменную таким способом, тело функции не сможет изменить значение этой переменной (то есть тело функции не сможет создать новую переменную, чтобы заменить существующую). Например, следующие назначения переменных будут существовать только в локальной области функции. Код вызова все еще будет видеть error == nil
,
- (NSArray*)executeFetchRequest:(Request *)request error:(NSError*)error {
...
error = [[[NSError alloc] init...] autorelease]; // local only
error = [[[SomeNSErrorSubclass alloc] init...] autorelease]; // local only
}
Причина № 2: экземпляры NSError неизменны
Давайте сохраним то же объявление функции, но вызовем функцию следующим образом:
NSError * error = [[[NSError alloc] init...] autorelease];
[someArray executeFetchRequest:someRequest error:error];
if (error != nil) { /* handle error */ }
Прежде всего, переменные правила области видимости гарантируют, что error
не может быть nil
, Итак if (error != nil) { ...
условие всегда будет истинным, но даже если вы захотите проверить конкретную информацию об ошибке внутри if
блок, вам не повезло, потому что случаи NSError
неизменны. Это означает, что после их создания вы не сможете изменять их свойства, поэтому функция не сможет изменить domain
или же userInfo
того, что NSError
экземпляр, который вы создали в коде вызова.
- (NSArray*)executeFetchRequest:(Request *)request error:(NSError*)error {
...
error.domain = ... // not allowed!
error.userInfo = ... // not allowed!
}
Это фактически другое возвращаемое значение. Ошибка не является доминирующей по соглашению в Какао, когда есть возвращаемое значение для операции. При обнаружении ошибки она может быть возвращена вам с помощью этого параметра out.
В случае NSError
, это работает так, потому что NSError
не является изменяемым типом - его поля устанавливаются при инициализации и никогда не изменяются. Поэтому вы не можете передать NSError
как обычно и установите код ошибки.