源码网商城,靠谱的源码在线交易网站 我的订单 购物车 帮助

源码网商城

iOS中Block的回调使用和解析详解

  • 时间:2020-03-04 16:16 编辑: 来源: 阅读:
  • 扫一扫,手机访问
摘要:iOS中Block的回调使用和解析详解
[b]Block 回调实现[/b] 先跟着我实现最简单的 Block 回调传参的使用,如果你能举一反三,基本上可以满足了 OC 中的开发需求。已经实现的同学可以跳到下一节。 首先解释一下我们例子要实现什么功能(其实是烂大街又最形象的例子): 有两个视图控制器 A 和 B,现在点击 A 上的按钮跳转到视图 B ,并在 B 中的textfield 输入字符串,点击 B 中的跳转按钮跳转回 A ,并将之前输入的字符串 显示在 A 中的 label 上。也就是说 A 视图中需要回调 B 视图中的数据。 想不明白的同学可以看一看最终实现的效果图: [img]http://files.jb51.net/file_images/article/201609/2016925140856044.gif?201682514910[/img] 这里不再对 Block 的语法做说明了。 首先,我们需要定义两个试图控制器 [code]AViewController [/code]和 [code]BViewController[/code],现在我们需要思考一下,Block 应该在哪里定义呢? 我们可以简单地这样思考,需要回调数据的是 A 视图,那么 Block 就应该在 B 中定义,用于获取传入回调数据。 因此我们在 [code]BViewController.h[/code] 中定义如下:
//BViewController.h
#import <UIKit/UIKit.h>

typedef void(^CallBackBlcok) (NSString *text);//1

@interface BViewController : UIViewController

@property (nonatomic,copy)CallBackBlcok callBackBlock;//2
@end
在这里,代码 1 用 typedef 定义了 [code]void(^) (NSString *text)[/code]的别名为 [code]CallBackBlcok [/code]。这样我们就可以在代码 2 中,使用这个别名定义一个 Block 类型的变量 [code]callBackBlock[/code]。 在定义了 [code]callBackBlock [/code]之后,我们可以在 B 中的点击事件中添加 [code]callBackBlock [/code]的传参操作:
//BViewController.m

- (IBAction)click:(id)sender {
 self.callBackBlock(_textField.text); //1
 [self.navigationController popToRootViewControllerAnimated:YES];
}
这样我们就可以在想要获取数据回调的地方,也就 A 的视图中调用 block:
// AViewController.m
- (IBAction)push:(id)sender {
 BViewController *bVC = [self.storyboard instantiateViewControllerWithIdentifier:@"BViewController"];

 bVC.callBackBlock = ^(NSString *text){ // 1

  NSLog(@"text is %@",text);

  self.label.text = text;

 };
 [self.navigationController pushViewController:bVC animated:YES];
}
代码 1 中,通过对回调将 B 中的数据传递到代码块中,并赋值给 A中的 label,实现了整个回调过程。 上例是通过将 block 直接赋值给 block 属性,也可以通过方法参数的方式传递 block 块。 [b]关于 Block 的疑惑[/b] 到目前为止,一切看起来都很美好(如果你照着上面的例子做的话),功能正常, A 视图中也获取到数据了。但是某些人可能就要说了,你的代码有问题,你的思路有问题,你这是误人子弟。 是的,代码的确还有问题,第一个问题就是循环引用的问题,在 A 视图的block 代码块中:
bVC.callBackBlock = ^(NSString *text){
  NSLog(@"text is %@",text);  
  self.label.text = text;  
 };
代码 [code]self.label.text = text;[/code] ,在 Block 中引用 self ,也就是 A ,而 A 创建并引用了 B ,而 B 引用 [code]callBackBlock[/code],此时就形成了一个循环引用,而编译器也不会报任何错误,我们需要非常小心这个问题(面试百分百问到我会乱说?)。此时我们通常的解决方法是使用弱引用来解除这个循环:
 __weak AViewController *weakSelf = self;
 bVC.callBackBlock = ^(NSString *text){ 
  NSLog(@"text is %@",text); 
//  self.label.text = text; 
  weakSelf.label.text = text;
 };
第二个问题是我自己对 Block 的理解不到位,我们都知道 Block 能截取自动变量,并且是不能在 Block 块中进行修改的(除非用__block修饰符),但是很明显 [code]weakSelf.label.text[/code]的值被修改了,并且没有用[code]__block[/code]修饰符, 这是为什么呢?因为 label 是个全局变量,而如果像如下的局部变量 a 是不能修改的,编译器也会报错: [img]http://files.jb51.net/file_images/article/201609/2016925141153727.jpg?201682514126[/img] 局部变量 通过这个小例子发现的两个问题,也算是值得了。 [b]Block 为什么能实现神奇的回调[/b] 在这里我不会说什么实现原理,仅仅是个人对 Block 能实现神奇回调的理解,有错误的地方请大家指出。 在先前使用 Block 的过程中,虽然会使用,但是总是有一个疑惑,简单说来就是: 为什么在 A 中的 block 块能调用到 B 中的数据? 回顾一下我们在 B 中所实现的代码,不外乎定义了一个 Block 变量,并在适当的时候传入参数,那么为什么在调用了  [code]self.callBackBlock(_textField.text)[/code] 之后,值就神奇传到了 A 中的 Block 块了呢? 通过整理使用的过程,我发现是我们的思维陷入了误区(可能是我个人),我们认为在 B 中传入[code] _textField.text[/code] 参数之后, A 中的 Block 块就可以获取到值。虽然思路是对的,但其实是不完整,导致我们形成了回调的数据是通过某种底层实现传递过去的错觉,这就使得我们认为这不需要深究。 事实是,通过简单的整理我们可以发现完整的回调流程应该是这样的: [img]http://files.jb51.net/file_images/article/201609/2016925141248332.jpg?2016825141256[/img] 回调流程 block 代码块赋值给 [code]bVC.callBackBlock[/code],此时 [code]callBackBlock [/code]的指针就指向这个代码块。 调用 [code]callBackBlock(NSString *text)[/code] 由于 [code]callBackBlock [/code]的指针是指向 A 中的 block 代码块,因此执行代码块的代码,实现回调。 很显然之前我忽略了代码块赋值给 [code]callBackBlock [/code]的这个操作(羞愧)。 现在再通过一段代码可以更清晰地理解这个原理:
 bVC.callBackBlock = ^(NSString *text){ //1
  NSLog(@"text is %@",text);
 };
 bVC.callBackBlock = ^(NSString *text){ //2
  NSLog(@"text b is %@",text);
 };
上述代码中,我们对 [code]callBackBlock[/code]进行了两次赋值,结果会怎么样呢? [img]http://files.jb51.net/file_images/article/201609/2016925141408364.jpg?2016825141420[/img] two block 可以看出来,Block 的回调只对代码 2 生效,因为[code]callBackBlock[/code]的指针最后指向了代码 2 的代码块。所以并没有什么神奇的魔法,也没什么隐藏的底层机制(这里指的是方便理解的底层)让你可以带着疑惑去使用它。 [b]总结[/b] 我这个人学习方法,总结起来就是看到新技术,先在自己的代码里跑一遍,能跑通,并且使用起来没有什么难度,就基本不会深究了。但是自我反思过,这样的学习方法是很不对的,写代码不能不求甚解,如果想要有所突破,不想局限于码农,一定要深入探究一下实现的机制,最起码要保证不带着疑惑去使用。以上就是这篇文章的全部内容,希望能对大家的学习或者工作带来一定的帮助,如果有疑问大家可以留言交流。
  • 全部评论(0)
联系客服
客服电话:
400-000-3129
微信版

扫一扫进微信版
返回顶部