Глава 7: Работа с данными в Objective-C ← Вернуться к списку глав Глава 9: Обработка ошибок и исключений

Глава 8: Многопоточность и асинхронность в Objective-C

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


8.1. Что такое многопоточность и почему она важна?

Представьте кухню ресторана

Почему многопоточность важна в приложениях


8.2. Основные понятия многопоточности

Потоки (Threads)

Асинхронность


8.3. Grand Central Dispatch (GCD)

Что такое GCD?

Основные понятия GCD


8.4. Использование GCD в Objective-C

Асинхронное выполнение задач

Пример:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(queue, ^{
    // Длительная задача
    NSLog(@"Начало задачи");
    [NSThread sleepForTimeInterval:3]; // Симуляция задержки
    NSLog(@"Задача завершена");
});

NSLog(@"Этот код выполняется сразу, не дожидаясь завершения задачи");

Вывод:

Этот код выполняется сразу, не дожидаясь завершения задачи Начало задачи Задача завершена

Обновление интерфейса после фоновой задачи

dispatch_async(queue, ^{
    // Фоновая задача
    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://example.com/data.json"]];
    
    // Возвращаемся в главный поток для обновления UI
    dispatch_async(dispatch_get_main_queue(), ^{
        // Обновляем интерфейс
        self.label.text = @"Данные загружены";
    });
});

8.5. Создание собственных очередей

Серийные и параллельные очереди

Пример использования серийной очереди

dispatch_async(serialQueue, ^{
    NSLog(@"Задача 1");
});

dispatch_async(serialQueue, ^{
    NSLog(@"Задача 2");
});

dispatch_async(serialQueue, ^{
    NSLog(@"Задача 3");
});

Вывод:

Задача 1 Задача 2 Задача 3

Пример использования параллельной очереди

dispatch_async(concurrentQueue, ^{
    NSLog(@"Задача A");
});

dispatch_async(concurrentQueue, ^{
    NSLog(@"Задача B");
});

dispatch_async(concurrentQueue, ^{
    NSLog(@"Задача C");
});

Вывод может быть любым:

Задача B Задача A Задача C

8.6. Синхронные и асинхронные задачи

Синхронное выполнение

dispatch_sync(queue, ^{
    NSLog(@"Синхронная задача");
});

NSLog(@"Этот код выполнится после завершения синхронной задачи");

Вывод:

Синхронная задача Этот код выполнится после завершения синхронной задачи

Асинхронное выполнение

dispatch_async(queue, ^{
    NSLog(@"Асинхронная задача");
});

NSLog(@"Этот код выполнится сразу, не дожидаясь завершения асинхронной задачи");

Вывод:

Этот код выполнится сразу, не дожидаясь завершения асинхронной задачи Асинхронная задача

8.7. NSOperation и NSOperationQueue

Что такое NSOperation?

NSOperationQueue

Пример использования NSOperation

Создание операций:

NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Операция 1");
}];

NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Операция 2");
}];

Добавление зависимостей:

[operation2 addDependency:operation1]; // Операция 2 выполнится после операции 1

Добавление операций в очередь:

NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue addOperations:@[operation1, operation2] waitUntilFinished:NO];

Вывод:

Операция 1 Операция 2

8.8. Группы задач и барьеры

Группы задач (dispatch_group)

Пример использования dispatch_group:

dispatch_group_t group = dispatch_group_create();

dispatch_group_enter(group);
dispatch_async(queue, ^{
    NSLog(@"Задача 1");
    dispatch_group_leave(group);
});

dispatch_group_enter(group);
dispatch_async(queue, ^{
    NSLog(@"Задача 2");
    dispatch_group_leave(group);
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"Все задачи завершены");
});

Вывод:

Задача 1 Задача 2 Все задачи завершены

Барьеры (dispatch_barrier)

Пример использования dispatch_barrier_async:

dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(concurrentQueue, ^{
    NSLog(@"Чтение данных 1");
});

dispatch_async(concurrentQueue, ^{
    NSLog(@"Чтение данных 2");
});

dispatch_barrier_async(concurrentQueue, ^{
    NSLog(@"Запись данных");
});

dispatch_async(concurrentQueue, ^{
    NSLog(@"Чтение данных 3");
});

Вывод:

Чтение данных 1 Чтение данных 2 Запись данных Чтение данных 3

8.9. Синхронизация и блокировки

Проблема доступа к общим ресурсам

Решение: синхронизация

Пример использования @synchronized:

@synchronized(self) {
    // Код внутри этого блока будет выполняться только одним потоком за раз
    sharedCounter++;
}

8.10. Многопоточность и блоки (Blocks)

Использование блоков с GCD

Пример:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // Фоновая задача
    NSData *data = ...; // Длительная операция
    
    dispatch_async(dispatch_get_main_queue(), ^{
        // Обновление интерфейса
        self.imageView.image = [UIImage imageWithData:data];
    });
});

Предостережение: циклы удержания (retain cycles)

Решение: использование слабой ссылки

__weak typeof(self) weakSelf = self;
dispatch_async(queue, ^{
    // Используем weakSelf вместо self
    [weakSelf doSomething];
});

8.11. Практические советы

Не блокируйте главный поток

Всегда возвращайтесь в главный поток для обновления UI

Будьте осторожны с синхронизацией

Избегайте дедлоков (deadlocks)

Пример дедлока:

dispatch_sync(dispatch_get_main_queue(), ^{
    // Это приведет к дедлоку, так как главный поток ждет сам себя
});

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

Задание 1: Асинхронная загрузка и отображение данных

  1. Создайте приложение, которое загружает данные из интернета в фоне.
  2. После загрузки обновите интерфейс, отобразив полученные данные.
  3. Убедитесь, что интерфейс остается отзывчивым во время загрузки.

Решение:

Задание 2: Создание серийной очереди для обработки задач

  1. Создайте серийную очередь.
  2. Добавьте в нее несколько задач, которые должны выполняться по порядку.
  3. Убедитесь, что задачи выполняются последовательно.

Решение:

Задание 3: Использование NSOperationQueue с зависимостями

  1. Создайте несколько операций, которые зависят друг от друга.
  2. Настройте зависимости между операциями.
  3. Добавьте операции в очередь и убедитесь, что они выполняются в правильном порядке.

Решение:


Заключение

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


Продолжение следует...

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

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

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

Глава 7: Работа с данными в Objective-C ← Вернуться к списку глав Глава 9: Обработка ошибок и исключений

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