Как избежать обширного кода обработки ошибок на каждом уровне в Objective C

В Objective C, как мне избежать всей этой обширной обработки ошибок в моих методах? Я читал, что Apple предлагает использовать ссылки NSError только тогда, когда ожидаются ошибки, но такой подход приводит к тому, что код полностью загромождается кодом обработки ошибок.

Руководство Apple по исключениям

В этом документе мне особенно бросается в глаза одно предложение;

библиотека синтаксического анализа может использовать внутренние исключения, чтобы указать на проблемы и обеспечить быстрый выход из состояния синтаксического анализа, которое может быть глубоко рекурсивным; однако вы должны позаботиться о том, чтобы перехватывать такие исключения на верхнем уровне библиотеки и преобразовывать их в соответствующий код возврата или состояние.

Это (неполный и неоптимизированный) фрагмент кода, демонстрирующий, что я имею в виду. Каждый вызов, который я делаю в своем методе, требует проверки ошибок на каждом этапе, загромождая код операторами if и затрудняя его чтение.

- (NSNumber) countFriendsForUserName:(NSString*) userName error:(NSError **)error {
    NSError *internalError = nil;
    Session *session = [_sessionMgr openSessionWithError:&internalError];
    if (!error) {
        User *user = [session findUserByName:userName error:&internalError];
       if (!error) {
           NSArray * bestFriends = [session getFriendsByUserId:user.id error:&internalError];
       }
    }
    [_sessionMgr closeSession];        
    if (internalError) {
        *error=internalError;
        return 0;
    } 
    return [bestFriends count];
}

Если бы вместо этого я использовал исключения, код выглядел бы примерно так (опять же, этот фрагмент кода — всего лишь неполный пример), в данный момент передо мной нет XCode.

- (NSNumber) countFriendsForUserName:(NSString*) userName error:(NSError **)error {
    @try{
        Session *session = [_sessionMgr openSession];
        User *user = [session findUserByName:userName];
        NSArray * bestFriends = [session getFriendsByUserId:user.id];
        [_sessionMgr closeSession];  
        return [bestFriends count];            
    } 
    @catch (NSException *e) {
        [_sessionMgr closeSession];        
        *error=[ExceptionParser createNSErrorFromException:e];
        return 0;
    } 
}

Если я правильно интерпретирую рекомендации Apple по исключениям, если мой код ведет себя как «библиотека синтаксического анализа» (пример Apple), может быть совершенно нормально использовать исключения внутри и просто переводить их, прежде чем я вернусь к вызывающей функции, что, в свою очередь, может воздействовать на мои коды NSError, чтобы предпринять соответствующие действия. Или я неправильно понял рекомендации Apple?


person David A    schedule 12.12.2014    source источник


Ответы (1)


Вы должны проверить возвращаемое значение метода, а не существование объекта NSError. Также вы можете связать объекты NSError, чтобы вернуть низкоуровневую ошибку вызывающей стороне:

- (NSInteger)countFriendsForUserName:(NSString*)userName
                               error:(NSError **)error
{
    NSArray *bestFriends = nil;
    Session *session = [_sessionMgr openSessionWithError:error];
    if (session) {
        User *user = [session findUserByName:userName error:error];
        if (user) {
           bestFriends = [session getFriendsByUserId:user.id error:error];
        }
    }
    [_sessionMgr closeSession];
    return [bestFriends count];
}

Примечание:

  • Перемещено bestFriends в начало метода.
  • Изменен тип возврата.
person Droppy    schedule 12.12.2014
comment
ты печатаешь быстрее меня ;) - person bryanmac; 12.12.2014
comment
Это очень важные моменты. Но мне все еще любопытно, почему исключения не были бы лучше? Код вообще не требует каких-либо операторов if, и на последнем шаге, перед возвратом результата в приложение (и пользователю), исключение будет преобразовано в NSError, который может интерпретировать пользовательский интерфейс приложения. - person David A; 12.12.2014
comment
Вы должны использовать исключения для исключительных условий. Если метод возвращает значение, и это возвращаемое значение равно NO или nil или чему-то еще, то это не обязательно ошибка. Это просто означает, что искомая вещь не существует, и это не ошибка, если вы хотите увидеть, нужно ли вам добавить эту вещь или нет. Исключения также дороги и громоздки для обработки на мелкозернистом уровне. - person Droppy; 12.12.2014
comment
Также обратите внимание, что ARC не защищен от исключений. У вас будет утечка памяти в ObjC, если вы будете генерировать исключения. Обычно это нормально, так как ожидается, что вы вылетите вскоре после создания исключения. Рекомендации в руководстве по обработке исключений предшествуют ARC. Если вы скомпилируете как ObjC++ (или если вы передадите определенные флаги компилятору), вы получите безопасные для памяти исключения, но это влияет на производительность (как память, так и время), и это не рекомендуется. - person Rob Napier; 15.12.2014