Почему здесь используется "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 как обычно и установите код ошибки.

Другие вопросы по тегам