Block 的本质是可以截取自动变量的匿名函数。
一.block的三种定义方式
1.返回值类型(^block的名字)(参数类型) = ^(参数类型和参数名) {};
void(^block)() = ^(){
NSLog(@"调用了block");
};
2.//返回值类型(^block的名字)(参数类型) = ^(参数类型和参数名) {};
//如果有参数,定义的时候,必须要写参数,而且必须要有参数变量名
int(^block)(int) = ^(int a){
return 1;
};
3.系统提供了一个定义block的宏
returnType(^blockName)(parameterTypes) = ^(parameters) {
// statements
// };
二.block类型
@property (nonatomic, strong) void(^block)();//这样在类中可以拿到self.block
//当然也可以取别名:
typedef void(^BlockType)();//BlockType不是变量名,而是这种类型的block的别名
//然后就可以这样
@property (nonatomic, strong) BlockType block;
三.block变量的传递
(1)如果是局部变量,Block是值传递
(2)如果是静态变量,全局变量,__block修饰的变量,block都是指针传递
@property (nonatomic, strong) CompletionBlock zfbBlock;
//block只能使用strong,不要使用copy
//因为当使用copy的时候,set方法是调用了copy帮你深拷贝一次,没有这个必要.
//就像NSString一样,他一般都是@"a"这种常量,没必要再去深拷贝一次,所以NSString常量也用strong不用copy.
__weak typeof (&*self) weakSelf= self;
/*
因为block会给内部的强指针对象进行一次强引用,比如常见的传入block中的self进行强引用
并且在self中,block又是strong的,self对block是强引用
所以,你强引用我,我强引用你,谁也不会被释放,就造成了循环引用
所以,为了避免循环引用,我们要在block使用self之前,进行这一步操作:
在block中使用weakSelf,就不会产生循环引用问题了.
使用了__weak修饰符的对象,作用等同于定义为weak的property。自然不会导致循环引用问题.
*/
Block 回调实现
首先解释一下我们例子要实现什么功能(其实是烂大街又最形象的例子):
有两个视图控制器 A 和 B,现在点击 A 上的按钮跳转到视图 B ,并在 B 中的textfield 输入字符串,点击 B 中的跳转按钮跳转回 A ,并将之前输入的字符串
显示在 A 中的 label 上。也就是说 A 视图中需要回调 B 视图中的数据。
我们可以简单地这样思考,需要回调数据的是 A 视图,那么 Block 就应该在 B 中定义,用于获取传入回调数据。
因此我们在 BViewController.h 中定义如下:
//BViewController.h#import
typedefvoid(^CallBackBlcok(NSString*text);
//1@interfaceBViewController:UIViewController
@property(nonatomic,copy)CallBackBlcok callBackBlock;//2
@end
在这里,代码 1 用 typedef 定义了void(^) (NSString *text)的别名为CallBackBlcok。这样我们就可以在代码 2 中,使用这个别名定义一个 Block 类型的变量callBackBlock。
在定义了callBackBlock之后,我们可以在 B 中的点击事件中添加callBackBlock的传参操作:
//BViewController.m
- (IBAction)click:(id)sender {
self.callBackBlock(_textField.text);//1[
self.navigationControllerpopToRootViewControllerAnimated:YES];}
这样我们就可以在想要获取数据回调的地方,也就 A 的视图中调用 block:
// AViewController.m
- (IBAction)push:(id)sender {
BViewController *bVC = [self.storyboardinstantiateViewControllerWithIdentifier:@"BViewController"]; bVC.callBackBlock= ^(NSString*text){
// 1NSLog(@"text is %@",text);
self.label.text= text;
};
[self.navigationControllerpushViewController:bVC animated:YES];}
代码 1 中,通过对回调将 B 中的数据传递到代码块中,并赋值给 A
中的 label,实现了整个回调过程。
上例是通过将 block 直接赋值给 block 属性,也可以通过方法参数的方式传递 block 块。
关于 Block 的疑惑
到目前为止,一切看起来都很美好(如果你照着上面的例子做的话),功能正常, A 视图中也获取到数据了。但是某些人可能就要说了,你的代码有问题,你的思路有问题,你这是误人子弟。
是的,代码的确还有问题,第一个问题就是循环引用的问题,在 A 视图的block 代码块中:
bVC.callBackBlock= ^(NSString*text){NSLog(@"text is %@",text);self.label.text= text; };
代码self.label.text = text;,在 Block 中引用 self ,也就是 A ,而 A 创建并引用了 B ,而 B 引用callBackBlock,此时就形成了一个循环引用,而编译器也不会报任何错误,我们需要非常小心这个问题(面试百分百问到我会乱说?)。此时我们通常的解决方法是使用弱引用来解除这个循环:
__weakAViewController*weakSelf =self; bVC.callBackBlock= ^(NSString*text){NSLog(@"text is %@",text);// self.label.text = text;weakSelf.label.text= text; };
第二个问题是我自己对 Block 的理解不到位,我们都知道 Block 能截取自动变量,并且是不能在 Block 块中进行修改的(除非用__block修饰符),但是很明显weakSelf.label.text的值被修改了,并且没有用__block修饰符, 这是为什么呢?因为label是个全局变量,而如果像如下的局部变量a是不能修改的,编译器也会报错:
局部变量
通过这个小例子发现的两个问题,也算是值得了。