block在iOS中是一个神奇的存在,既像是面向过程的函数又像是面向对象的对象实例,实质上是一个匿名函数.如果能够很好的利用它就就可以很方面地控制数据流程,使代码结构更加清晰,逻辑实现更加明了易读.
1 如何定义一个Block
- 直接在使用的地方定义
例如我们需要一个没有返回值没有参数的block:如果参数为空,后边的()可以省略.
void (^blockWithNoValueAndParameter)(void) = ^(){ //code here do what you want }; 或者 void (^blockWithNoValueAndParameter)(NSString *) = ^{ //code here do what you want };
或者需要一个有参数无返回值的block:
void(^blockWithNoValueBlock)(NSString *) = ^(NSString *str) { NSLog("content == %@", str); };
或者有参数有返回值的block:
NSString *(^block)(NSString *) = ^NSString *(NSString *str) { NSString *_str = str; if (![_str hasPrefix:@"_aspects_"]) { _str = [@"_aspects_" stringByAppendingString:_str]; } return _str; };
- 使用typedef预先定义block
typedef NSInteger (^GetLengthBlock)(NSString *); GetLengthBlock block = ^NSInteger(NSString *str){ return str.length; };
可能有人注意到了,为什么在上边的定义中我们都使用了小写开头,而这里使用大些开头呢?这是因为typedef定义的是一个"类"(此时的GetLengthBlock相当于 NSInteger (^)(NSString *) 这个结构),而不是一个具体的block,而上边我们只是定义一个具体的block(相当于 (NSInteger (^)(NSString *))block),虽然看起来差不多,但事实上代表的意义是不一样的.
2. Block的使用场景
2.1 作为属性进行传值
例如我们可以在一个控制器中定义一个属性block,在给界面push之前定义block的实现,然后在该界面发生的事件就可以通过block进行回传.
@interface ViewController () //由于block绝大多数情况下会需要捕获外部变量,所以一般使用block修饰,将block从栈区copy到堆区,方便控制其生命周期 @property (copy, nonatomic) void(^sendValueBlock)(id); @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; } -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { if (self.sendValueBlock) { self.sendValueBlock(@"touch"); } }
注意:我们将block视为对象,由于block绝大多数情况下会需要捕获外部变量,所以一般使用block修饰,将block从栈区copy到堆区,方便控制其生命周期,防止其提前释放造成野指针异常.
2.2 作为参数进行回调
例如,AFNetworking中AFHTTPSessionManager中GET方式请求的数据回调,可以在需要的时候进行回调.
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString parameters:(nullable id)parameters progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
2.3 实现链式编程
可以通过定义属性block来实现函数的链式调用,例如著名的布局框架masnory就是借助于block实现的,我们这里制作简单的说明.例如我们做一个简单的加减法运算器:
@interface Calculator : NSObject @property (assign, nonatomic) double result; - (Calculator *(^)(double num))add; - (Calculator *(^)(double num))sub; @end @implementation Calculator - (Calculator *(^)(double num))add { return ^Calculator *(double num) { self.result += num; return self; }; } - (Calculator *(^)(double num))sub { return ^Calculator *(double num){ self.result -= num; return self; }; } @end Calculator *cal = [[Calculator alloc] init]; cal.add(30).sub(3).add(23);