Добро пожаловать в пятую главу нашего путешествия по Objective-C! В этой главе мы познакомимся с блоками (Blocks) — мощным инструментом, который позволяет писать более гибкий и лаконичный код. Блоки могут показаться сложными на первый взгляд, но мы объясним все простым языком, чтобы вы легко во всем разобрались.
Блок — это кусок кода, который можно передать и выполнить позже. Можно представить блок как небольшую функцию, которую можно хранить в переменной, передавать в качестве аргумента или возвращать из другой функции.
Синтаксис:
return_type (^block_name)(parameter_types) = ^(parameters) {
// Код блока
};
return_type
: тип данных, который блок возвращает.block_name
: имя переменной блока.parameter_types
: типы параметров блока.parameters
: параметры блока.int (^additionBlock)(int, int) = ^(int a, int b) {
return a + b;
};
additionBlock
, который принимает два int
и возвращает их сумму.int result = additionBlock(5, 3); // result будет 8
void (^helloBlock)(void) = ^{
NSLog(@"Привет, блок!");
};
helloBlock(); // Вывод: "Привет, блок!"
helloBlock
, который ничего не принимает и ничего не возвращает.Блоки часто используются в качестве параметров методов или функций, чтобы выполнить определенное действие позже.
Пример:
- (void)performOperationWithBlock:(void (^)(void))operationBlock {
NSLog(@"Начинаем операцию...");
operationBlock();
NSLog(@"Операция завершена.");
}
Использование:
[self performOperationWithBlock:^{
NSLog(@"Выполняем важную задачу.");
}];
Вывод:
Блоки могут "захватывать" переменные из окружающего контекста, то есть использовать переменные, объявленные вне блока.
Пример:
int multiplier = 5;
int (^multiplyBlock)(int) = ^(int num) {
return num * multiplier;
};
int result = multiplyBlock(10); // result будет 50
multiplyBlock
использует переменную multiplier
, которая объявлена вне блока.По умолчанию переменные, захваченные блоком, являются константными, то есть их нельзя изменить внутри блока. Однако, если вам нужно изменить значение, вы можете использовать модификатор __block
.
Пример:
__block int total = 0;
void (^addBlock)(int) = ^(int num) {
total += num;
};
addBlock(5);
addBlock(10);
NSLog(@"Total: %d", total); // Вывод: "Total: 15"
__block
, мы можем изменять переменную total
внутри блока.Блоки могут создавать циклы удержания, если они захватывают self
(объект класса) сильной ссылкой.
Проблема:
self.myBlock = ^{
[self doSomething];
};
self
, а self
удерживает блок через свойство myBlock
.Чтобы избежать цикла удержания, используйте слабую ссылку на self
.
Пример:
__weak typeof(self) weakSelf = self;
self.myBlock = ^{
[weakSelf doSomething];
};
self
, и цикл удержания не возникает.Пример:
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)"
sortedArrayUsingComparator:
принимает блок для сравнения элементов.Блоки используются для определения действий, которые должны быть выполнены во время или после анимации.
Пример:
[UIView animateWithDuration:0.5 animations:^{
// Анимационные изменения
view.alpha = 0.0;
} completion:^(BOOL finished) {
// Действия после завершения анимации
[view removeFromSuperview];
}];
Блоки часто используются для выполнения кода после завершения асинхронной задачи, такой как загрузка данных из интернета.
Пример:
- (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(@"Данные получены");
}];
Чтобы упростить код, можно определить тип блока с помощью 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(@"Произошла ошибка");
}
}];
CompletionBlock
как тип блока в нескольких местах.Пример:
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. Теперь вы знаете, что такое блоки, как их объявлять, использовать и передавать в методы и функции. Блоки — мощный инструмент, который позволяет писать более гибкий и лаконичный код, особенно при работе с асинхронными задачами и обработкой событий.
Решение:
- (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];
Calculator
с методом calculateWithBlock:
, который принимает блок с двумя числами и возвращает результат.Решение:
@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"
Решение:
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, и научитесь эффективно использовать их в своих проектах.
Не забывайте практиковаться! Работа с данными — ключевой аспект разработки приложений. Чем больше вы будете экспериментировать и писать код, тем лучше будете понимать, как создавать мощные и полезные приложения.
Удачи и до встречи в следующей главе!
Просмотров: 43