Глава 8: Многопоточность и асинхронность в Objective-C ← Вернуться к списку глав Глава 10: Работа с протоколами и делегатами

Глава 9: Обработка ошибок и исключений

Добро пожаловать в девятую главу нашего увлекательного путешествия по Objective-C! В этой главе мы поговорим об обработке ошибок и исключений. Вы узнаете, как сделать ваши приложения более надежными, научитесь предугадывать возможные проблемы и правильно на них реагировать. Не волнуйтесь, мы объясним все простым и понятным языком, чтобы вы могли легко во всем разобраться.


9.1. Почему обработка ошибок важна?

Представьте, что ваш код — это сложный механизм

Что может пойти не так?


9.2. Ошибки и исключения: в чем разница?

Ошибки (Errors)

Исключения (Exceptions)


9.3. Обработка ошибок с помощью NSError

Что такое NSError?

Структура NSError

Использование NSError в методах

Пример метода с NSError:

- (BOOL)saveData:(NSData *)data toFile:(NSString *)path error:(NSError **)error;

Пример обработки ошибок с NSError

Шаг 1: Вызов метода и получение ошибки

NSError *error = nil;
BOOL success = [self saveData:data toFile:filePath error:&error];

Шаг 2: Проверка результата и обработка ошибки

if (!success) {
    NSLog(@"Ошибка при сохранении данных: %@", error.localizedDescription);
    // Дополнительные действия по обработке ошибки
} else {
    NSLog(@"Данные успешно сохранены");
}

Создание NSError внутри метода

Пример:

- (BOOL)saveData:(NSData *)data toFile:(NSString *)path error:(NSError **)error {
    // Попытка сохранения данных
    BOOL success = [data writeToFile:path options:0 error:error];
    
    if (!success) {
        // Если произошла ошибка и указатель error не равен nil
        if (error != NULL) {
            *error = [NSError errorWithDomain:@"com.example.MyApp"
                                         code:1001
                                     userInfo:@{NSLocalizedDescriptionKey: @"Не удалось сохранить данные"}];
        }
    }
    return success;
}

9.4. Обработка исключений с помощью @try, @catch, @finally

Что такое исключения?

Синтаксис обработки исключений

@try {
    // Код, который может вызвать исключение
}
@catch (NSException *exception) {
    // Обработка исключения
}
@finally {
    // Код, который выполнится в любом случае
}

Пример использования @try, @catch

@try {
    NSArray *array = @[@1, @2, @3];
    NSLog(@"Элемент: %@", array[5]); // Индекс вне диапазона, вызовет исключение
}
@catch (NSException *exception) {
    NSLog(@"Произошло исключение: %@", exception.reason);
}
@finally {
    NSLog(@"Блок finally выполнен");
}

Вывод:

Произошло исключение: *** -[__NSArrayI objectAtIndexedSubscript:]: index 5 beyond bounds [0 .. 2] Блок finally выполнен

Когда использовать исключения?

Предупреждение


9.5. Генерация собственных исключений

Как создать свое исключение

Пример:

- (void)processData:(NSData *)data {
    if (data == nil) {
        @throw [NSException exceptionWithName:NSInvalidArgumentException
                                       reason:@"Данные не могут быть nil"
                                     userInfo:nil];
    }
    // Обработка данных
}

Обработка собственного исключения

@try {
    [self processData:nil];
}
@catch (NSException *exception) {
    NSLog(@"Ошибка: %@", exception.reason);
}

9.6. Использование @throw и @finally

Перебрасывание исключений

Пример:

@try {
    // Код
}
@catch (NSException *exception) {
    // Локальная обработка
    @throw; // Перебрасываем исключение дальше
}

Блок @finally

Пример:

NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:@"data.txt"];

@try {
    NSData *data = [fileHandle readDataToEndOfFile];
    // Обработка данных
}
@catch (NSException *exception) {
    NSLog(@"Ошибка чтения файла: %@", exception.reason);
}
@finally {
    [fileHandle closeFile];
    NSLog(@"Файл закрыт");
}

9.7. Проверка аргументов и утверждения (Assertions)

NSAssert

Синтаксис:

NSAssert(condition, @"Сообщение об ошибке");

Пример:

- (void)setAge:(NSInteger)age {
    NSAssert(age >= 0, @"Возраст не может быть отрицательным");
    _age = age;
}

NSParameterAssert

Пример:

- (void)processString:(NSString *)string {
    NSParameterAssert(string != nil);
    // Дальнейшая обработка строки
}

9.8. Практические советы по обработке ошибок

Используйте NSError для ожидаемых ошибок

Избегайте злоупотребления исключениями

Всегда проверяйте возвращаемые значения

Пример:

NSString *fileContents = [NSString stringWithContentsOfFile:@"data.txt" encoding:NSUTF8StringEncoding error:&error];

if (fileContents == nil) {
    NSLog(@"Ошибка чтения файла: %@", error.localizedDescription);
}

Предоставляйте полезную информацию об ошибках

Пример:

if (error != NULL) {
    *error = [NSError errorWithDomain:@"com.example.MyApp"
                                 code:1002
                             userInfo:@{NSLocalizedDescriptionKey: @"Не удалось загрузить данные с сервера"}];
}

9.9. Логирование и отладка ошибок

Используйте NSLog для вывода сообщений

Пример:

NSLog(@"Ошибка: %@", error.localizedDescription);

Используйте инструменты отладки


9.10. Практические задания

Задание 1: Обработка ошибок при чтении файла

  1. Напишите метод readTextFromFileAtPath:error:, который читает текст из файла по указанному пути.
  2. Если файл не найден, метод должен вернуть nil и заполнить NSError.
  3. В вызывающем коде обработайте возможную ошибку и выведите сообщение в консоль.

Решение:

- (NSString *)readTextFromFileAtPath:(NSString *)path error:(NSError **)error {
    NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:error];
    if (content == nil && error != NULL) {
        *error = [NSError errorWithDomain:@"com.example.MyApp"
                                     code:1003
                                 userInfo:@{NSLocalizedDescriptionKey: @"Не удалось прочитать файл"}];
    }
    return content;
}

// Использование:
NSError *error = nil;
NSString *text = [self readTextFromFileAtPath:@"path/to/file.txt" error:&error];

if (text == nil) {
    NSLog(@"Ошибка: %@", error.localizedDescription);
} else {
    NSLog(@"Содержимое файла: %@", text);
}

Задание 2: Проверка параметров метода

  1. Создайте метод divideNumber:byNumber:, который возвращает результат деления.
  2. Если знаменатель равен нулю, метод должен генерировать исключение.
  3. Обработайте исключение в вызывающем коде и выведите сообщение в консоль.

Решение:

- (double)divideNumber:(double)numerator byNumber:(double)denominator {
    if (denominator == 0) {
        @throw [NSException exceptionWithName:NSInvalidArgumentException
                                       reason:@"Деление на ноль невозможно"
                                     userInfo:nil];
    }
    return numerator / denominator;
}

// Использование:
@try {
    double result = [self divideNumber:10 byNumber:0];
    NSLog(@"Результат: %f", result);
}
@catch (NSException *exception) {
    NSLog(@"Ошибка: %@", exception.reason);
}

Задание 3: Использование NSAssert

  1. Создайте метод setUsername:, который устанавливает имя пользователя.
  2. Используйте NSAssert, чтобы проверить, что имя не пустое и не равно nil.
  3. Вызовите метод с пустой строкой и убедитесь, что приложение аварийно завершается с сообщением об ошибке.

Решение:

- (void)setUsername:(NSString *)username {
    NSAssert(username != nil && [username length] > 0, @"Имя пользователя не может быть пустым");
    _username = username;
}

// Использование:
[self setUsername:@""]; // Приведет к аварийному завершению с сообщением об ошибке

Заключение

Поздравляем! Вы успешно освоили основы обработки ошибок и исключений в Objective-C. Теперь вы знаете, как правильно реагировать на возможные проблемы, делать ваши приложения более надежными и обеспечивать лучший пользовательский опыт. Обработка ошибок — важная часть разработки, и эти знания помогут вам создавать качественные приложения.


Что дальше?

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

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

Удачи и до встречи в следующей главе!

Глава 8: Многопоточность и асинхронность в Objective-C ← Вернуться к списку глав Глава 10: Работа с протоколами и делегатами

Просмотров: 42