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();
9. 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"); };