iOS - Block的写法及使用
Block技术合集
iOS - Block变量截获
1. Block定义
//声明定义
int(^ sumOfNumbers)(int a, int b) = ^(int a, int b) {
return a + b;
};
int sum = sumOfNumbers(1, 2);
NSLog(@"sum = %d", sum);
//打印
2021-05-01 11:04:05.920543+0800 BlockDemo[18351:6429226] sum = 3
这段代码等号左侧声明一个名为sumOfNumbers的代码块,名称前用^符号表示后面的字符串是block的名称。最左侧的int表示这个block的返回值,括号中间表示这个block的参数列表,这里接收两个int类型的参数。 而在等号右侧表示这个block的定义,其中返回值是可以省略的,编译器会根据上下文自动补充返回值类型。使用^符号衔接着一个参数列表,使用括号包起来,告诉编译器这是一个block,然后使用大括号将block的代码封装起来。
Block
2. Block写法
return_type
表示返回的对象/关键字等(通常是void
)blockName
表示block
的名称var_type
表示参数的类型varName
表示参数名称
1. 做为局部变量
return_type (^blockName)(var_type) = ^return_type (var_type varName) {
// ...
};
blockName(var);
int(^ sumOfNumbers)(int a, int b) = ^(int a, int b) {
return a + b;
};
2. 做为属性
@property (copy) return_type (^blockName) (var_type);
@property (nonatomic, copy) void(^ didLoginSuccess)(NSString *username);
@property (nonatomic, copy) void(^ didLoginFailed)(void);
3. 定义方法时,做为Block型的形参数
- (void)yourMethod:(return_type (^)(var_type))blockName;
- (void)sumOfA:(int)a B:(int)b sumBlock:(void(^)(int sum))sumBlock {
int sum = a + b;
sumBlock(sum);
}
- (void)login:(void(^)(void))completion {
completion();
}
4. Block作为实参
Block作为参数使用,常见于各框架之中,比如在封装一个类时,当做什么事情由外界去决定,什么时候调用由自己的类决定时,这时候就需要将block作为参数使用。
[someObject doSomethingWithBlock: ^return_type (var_type varName)
{
//...
}];
以自定义一个简单的工具类CalculatorManager为例:
//
// CalculatorManger.h
// BlockDemo
//
// Created by Ternence on 2021/5/1.
//
#import <Foundation/Foundation.h>
@interface CalculatorManger : NSObject
//计算结果值
@property (nonatomic, assign) int result;
+ (instancetype)sharedManager;
//block作为参数时格式与其它类型定义时一致,都是(类型)变量名,看起来有些晕人
- (void)calculate:(int(^)(int))calculateBlock;
@end
//
// CalculatorManger.m
// BlockDemo
//
// Created by Ternence on 2021/5/1.
//
#import "CalculatorManger.h"
static int defaultHeight = 100;
static CalculatorManger * instance = nil;
@implementation CalculatorManger
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (instance == nil) {
instance = [super allocWithZone:zone];
}
});
return instance;;
}
+ (instancetype)sharedManager {
return [[self alloc] init];
}
//方法中定义了一个block数据类型参数(返回值为int类型的,且带有一个int类型的形参)
- (void)calculate:(int (^)(int))calculateBlock {
//calculateBlock接受外界传入的代码块,也就意味着怎么去操作是由外界调用者决定的
_result = calculateBlock(defaultHeight);
}
@end
外界控制器调用
CalculatorManger *manager = [CalculatorManger sharedManager];
[manager calculate:^int(int i) {
NSLog(@"=======i : %d", i);
for (int j = 0; j < 100; j ++) {
i++;
}
return i;
}];
NSLog(@"=====result: %d", manager.result);
可以看到,工具类CalculcatorManager的计算方法calculate:^int(int)calculateBlock其具体实现,交由了外界的控制器调用者去决定了。虽然有些许绕,但只要搞清楚block的作为参数使用时的格式,理解起来也很快的,如果先前对这类型的用法在理解上抱有疑惑的话,希望这个小例子能帮到您:)
5. 匿名Block
Block实现时,如上文的局部变量和实参,等号右边就是一个匿名Block,它没有blockName:
^return_type (var_type varName)
{
//...
};
6. typedef Block
利用typedef简化Block的声明
typedef return_type (^BlockTypeName)(var_type);
typedef void (^didQuitAcountBlock)(void);
使用
BlockTypeName aBlock = ^return_type (var_type) {
//...
}
didQuitAcountBlock quitBlock = ^void(void) {
NSLog(@"did quit acount");
};
quitBlock();
7. 内联 Block
这种形式并不常用,匿名Block声明后立即被调用,内联 Block可用于代码分块,提高代码可读性,功能类似大括号的代码块,其它功能非常有限
^return_type (var_type varName)
{
//...
}(var);
^void() {
NSLog(@"内联Block声明后即被调用");
}();
8. 递归调用Block
Block内部调用自身,递归调用是很多算法基础,特别是在无法提前预知循环终止条件的情况下。
注意 由于Block内部引用了自身,这里必须使用__block
避免保留环问题。
__block return_type (^blockName)(var_type) = [^return_type (var_type varName)
{
if (returnCondition)
{
blockName = nil;
return;
}
// ...
blockName(varName);
} copy];
blockName(varValue);
实例
__block int i = 5;
__block void (^ dismisssController)(void) = [^void(void) {
if (i == 0) {
dismisssController = nil;
return;
}
NSLog(@"recursionBlock i = %d", i);
i --;
dismisssController();
} copy];
dismisssController();
8. Block做为返回值
- (return_type(^)(var_type))methodName
{
// ...
}
3. 一个Block传参的例子
.h
@interface SecondViewController : UIViewController
@property (nonatomic, copy) void(^ didLoginSuccess)(NSString *username);
@property (nonatomic, copy) void(^ didLoginFailed)(void);
@end
.m
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
if (self.didLoginSuccess) {
self.didLoginSuccess(@"码代码的小马");
}
if (self.didLoginFailed) {
self.didLoginFailed();
}
}
调用block
SecondViewController *vc = [[SecondViewController alloc] init];
[self.navigationController pushViewController:vc animated:true];
vc.didLoginSuccess = ^(NSString * _Nonnull username) {
NSLog(@"congratulations %@! welcome to objc world", username);
};
vc.didLoginFailed = ^{
NSLog(@"login failed");
};