Глава 4: Управление памятью в Objective-C ← Вернуться к списку глав Глава 6: Фреймворк Foundation

Глава 5: Блоки (Blocks) в Objective-C

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


5.1. Что такое блоки?

Простыми словами о блоках

Блок — это кусок кода, который можно передать и выполнить позже. Можно представить блок как небольшую функцию, которую можно хранить в переменной, передавать в качестве аргумента или возвращать из другой функции.

Зачем нужны блоки?


5.2. Синтаксис блоков

Объявление и определение блока

Синтаксис:

return_type (^block_name)(parameter_types) = ^(parameters) {
    // Код блока
};

Простой пример

int (^additionBlock)(int, int) = ^(int a, int b) {
    return a + b;
};

Вызов блока

int result = additionBlock(5, 3); // result будет 8

5.3. Блоки без параметров и возвращаемого значения

Блок без параметров

void (^helloBlock)(void) = ^{
    NSLog(@"Привет, блок!");
};

helloBlock(); // Вывод: "Привет, блок!"

5.4. Блоки как параметры методов и функций

Передача блока в метод

Блоки часто используются в качестве параметров методов или функций, чтобы выполнить определенное действие позже.

Пример:

- (void)performOperationWithBlock:(void (^)(void))operationBlock {
    NSLog(@"Начинаем операцию...");
    operationBlock();
    NSLog(@"Операция завершена.");
}

Использование:

[self performOperationWithBlock:^{
    NSLog(@"Выполняем важную задачу.");
}];

Вывод:

Начинаем операцию... Выполняем важную задачу. Операция завершена.

5.5. Блоки и переменные окружения

Захват переменных

Блоки могут "захватывать" переменные из окружающего контекста, то есть использовать переменные, объявленные вне блока.

Пример:

int multiplier = 5;
int (^multiplyBlock)(int) = ^(int num) {
    return num * multiplier;
};

int result = multiplyBlock(10); // result будет 50

Изменение переменных внутри блока

По умолчанию переменные, захваченные блоком, являются константными, то есть их нельзя изменить внутри блока. Однако, если вам нужно изменить значение, вы можете использовать модификатор __block.

Пример:

__block int total = 0;
void (^addBlock)(int) = ^(int num) {
    total += num;
};

addBlock(5);
addBlock(10);

NSLog(@"Total: %d", total); // Вывод: "Total: 15"

5.6. Блоки и память

Циклы удержания с блоками

Блоки могут создавать циклы удержания, если они захватывают self (объект класса) сильной ссылкой.

Проблема:

self.myBlock = ^{
    [self doSomething];
};

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

Чтобы избежать цикла удержания, используйте слабую ссылку на self.

Пример:

__weak typeof(self) weakSelf = self;
self.myBlock = ^{
    [weakSelf doSomething];
};

5.7. Блоки в стандартных методах и фреймворках

Сортировка массива с помощью блоков

Пример:

NSArray *numbers = @[@3, @7, @1, @5];

NSArray *sortedNumbers = [numbers sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    return [obj1 compare:obj2];
}];

NSLog(@"%@", sortedNumbers); // Вывод: "(1, 3, 5, 7)"

Анимации

Блоки используются для определения действий, которые должны быть выполнены во время или после анимации.

Пример:

[UIView animateWithDuration:0.5 animations:^{
    // Анимационные изменения
    view.alpha = 0.0;
} completion:^(BOOL finished) {
    // Действия после завершения анимации
    [view removeFromSuperview];
}];

5.8. Блоки и асинхронные задачи

Блоки часто используются для выполнения кода после завершения асинхронной задачи, такой как загрузка данных из интернета.

Пример:

- (void)fetchDataWithCompletion:(void (^)(NSData *data))completionBlock {
    // Симулируем асинхронную загрузку
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Загрузка данных
        NSData *data = ...; // Полученные данные
        
        // Возвращаемся в главный поток для обновления UI
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completionBlock) {
                completionBlock(data);
            }
        });
    });
}

[self fetchDataWithCompletion:^(NSData *data) {
    // Обрабатываем полученные данные
    NSLog(@"Данные получены");
}];

5.9. Типы блоков и сокращения

Определение типа блока

Чтобы упростить код, можно определить тип блока с помощью typedef.

Пример:

typedef void (^CompletionBlock)(BOOL success);

- (void)performTaskWithCompletion:(CompletionBlock)completion {
    // Выполняем задачу
    BOOL success = YES;
    
    if (completion) {
        completion(success);
    }
}

[self performTaskWithCompletion:^(BOOL success) {
    if (success) {
        NSLog(@"Задача выполнена успешно");
    } else {
        NSLog(@"Произошла ошибка");
    }
}];

5.10. Практические примеры использования блоков

Фильтрация массива

Пример:

NSArray *numbers = @[@1, @2, @3, @4, @5, @6];

NSIndexSet *evenIndexes = [numbers indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
    return ([obj intValue] % 2 == 0);
}];

NSArray *evenNumbers = [numbers objectsAtIndexes:evenIndexes];

NSLog(@"%@", evenNumbers); // Вывод: "(2, 4, 6)"

Обработка событий с блоками

Блоки часто используются в качестве обратных вызовов (callbacks) для обработки событий.

Пример:

UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];

[button addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];

// ...

- (void)buttonTapped:(UIButton *)sender {
    NSLog(@"Кнопка нажата");
}
[button addActionBlock:^(UIButton *sender) {
    NSLog(@"Кнопка нажата");
} forControlEvents:UIControlEventTouchUpInside];

Заключение

Поздравляем! Вы познакомились с блоками в Objective-C. Теперь вы знаете, что такое блоки, как их объявлять, использовать и передавать в методы и функции. Блоки — мощный инструмент, который позволяет писать более гибкий и лаконичный код, особенно при работе с асинхронными задачами и обработкой событий.


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

Задание 1: Приветствие с задержкой

  1. Создайте метод, который принимает блок и задержку в секундах.
  2. После указанной задержки метод должен выполнить блок.
  3. Используйте метод, чтобы вывести сообщение "Привет через 3 секунды!".

Решение:

- (void)performBlock:(void (^)(void))block afterDelay:(NSTimeInterval)delay {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC), dispatch_get_main_queue(), block);
}

[self performBlock:^{
    NSLog(@"Привет через 3 секунды!");
} afterDelay:3.0];

Задание 2: Калькулятор с блоками

  1. Создайте класс Calculator с методом calculateWithBlock:, который принимает блок с двумя числами и возвращает результат.
  2. Используйте метод для выполнения операций сложения, вычитания, умножения и деления.

Решение:

@interface Calculator : NSObject

- (int)calculateWithBlock:(int (^)(int a, int b))operationBlock a:(int)a b:(int)b;

@end

@implementation Calculator

- (int)calculateWithBlock:(int (^)(int a, int b))operationBlock a:(int)a b:(int)b {
    return operationBlock(a, b);
}

@end

// Использование:

Calculator *calculator = [[Calculator alloc] init];

int sum = [calculator calculateWithBlock:^int(int a, int b) {
    return a + b;
} a:5 b:3];

NSLog(@"Сумма: %d", sum); // Вывод: "Сумма: 8"

Задание 3: Фильтрация строк по длине

  1. Создайте массив строк.
  2. Используя блоки, отфильтруйте массив, оставив только строки длиной более 5 символов.
  3. Выведите результат.

Решение:

NSArray *words = @[@"Objective-C", @"Swift", @"Java", @"Python", @"C"];

NSIndexSet *longWordsIndexes = [words indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
    return ([obj length] > 5);
}];

NSArray *longWords = [words objectsAtIndexes:longWordsIndexes];

NSLog(@"%@", longWords); // Вывод: "(Objective-C, Python)"

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

В следующей главе мы подробно рассмотрим фреймворк Foundation. Вы узнаете о его основных классах, таких как NSString, NSArray, NSDictionary, и научитесь эффективно использовать их в своих проектах.

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

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

Глава 4: Управление памятью в Objective-C ← Вернуться к списку глав Глава 6: Фреймворк Foundation

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