Block从简单到高级的使用,以及项目中经常使用的场景

Block从简单到高级的使用,以及项目中经常使用的场景

一、Block的简单使用

       使用Block的三个步骤:1.定义Block变量;2.定义Block(即创建block代码块)3.调用block匿名函数;

1>以下是使用的两个简单的例子:

无参数,无返回值


有参数有返回值


2>定义Block变量的时候一般大家都是用typedef重定义,应用起来既方便看起来又很直观,例如上面两个例子可以如下书写:





3>Block变量在ARC和MRC环境下的生命周期


ARC环境下打印结果如下:


分析原因如下:

 //1.分析,ARC如果在块对象中使用了__block指定的变量,那么这个变量将会被copy到堆内存中,并且原变量也会指向这个堆内存中的空间

//2.如果有两个块对象引用了同一个__block指定的变量,那么他们共享这个变量,共享同一个内存


MRC环境下打印结果如下:



分析原因如下:


//1.函数内被__block修饰的变量可以被块对象读写,多个块对象之间可以共享__block变量的值

//2.__block变量不是静态变量,在块句法每次用到这种变量的时候会去相应的内存空间获取值,就是说不同块对象分享的__block变量的值是执行时动态生成

//3.访问__block变量的块对象被复制后,新生成的块对象也能共享__block变量的值

//4.多个块对象访问同一个__block变量的时候,只要有一个块对象存在,__block变量就会存在;如果访问__block变量的对象都不存在了,__block对象随之消失


//总结:

//注意:因为__block变量的内存位置可能会发生变化,所以,写程序时候不要写用指针访问__block变量的代码,那样可能会得不到预期的结果(这是在项目中尤为重要的一点)

二、iOS开发中,项目中使用Block

1.Block作为属性

[objc]  view plain  copy
  1. @interface ViewController ()  
  2. {  
  3.     ///名字按钮  
  4.     UIButton *_nameBtn;  
  5. }  
  6.   
  7. @end  
  8.   
  9. @implementation ViewController  
  10.   
  11. - (void)viewDidLoad {  
  12.     [super viewDidLoad];  
  13.     _nameBtn = [UIFactory buttonWithTitle:@"跑男" withRect:CGRectMake(5010010050) withBackgroundColor:[UIColor redColor]];  
  14.     [_nameBtn addTarget:self action:@selector(run:) forControlEvents:UIControlEventTouchUpInside];  
  15.     [self.view addSubview:_nameBtn];  
  16.   
  17. }  
  18.   
  19. -(void)run:(UIButton *)sender  
  20. {  
  21.     [self pushNextVCWithBlock:^(NSString *name) {  
  22.         [_nameBtn setTitle:name forState:UIControlStateNormal];  
  23.     }];  
  24.     NSLog(@"第一次");  
  25. }  
  26. -(void)pushNextVCWithBlock:(testBlock)testBlock  
  27. {  
  28.     RunViewController  *runVC = [[RunViewController alloc]init];  
  29.     //block作为属性传给下一个界面  
  30.     runVC.testBlock = testBlock;  
  31.     [self.navigationController pushViewController:runVC animated:YES];  
  32. }  


[objc]  view plain  copy
  1. @interface RunViewController : UIViewController  
  2.   
  3. ///Block作为属性<pre name="code" class="objc">@property(nonatomic,strong)testBlock testBlock;  
[objc]  view plain  copy
  1. </pre><pre name="code" class="objc"><pre name="code" class="objc">@end  

 
  
 
  
[objc]  view plain  copy
  1. #import "RunViewController.h"  
  2. @interface RunViewController ()  
  3. {  
  4.     ///返回按钮  
  5.     UIButton * _backBtn;  
  6. }  
  7. @end  
  8. @implementation RunViewController  
  9.   
  10. -(void)viewDidLoad  
  11. {  
  12.     [super viewDidLoad];  
  13.       
  14.     _backBtn = [UIFactory buttonWithTitle:@"美女" withRect:CGRectMake(5010010050) withBackgroundColor:[UIColor redColor]];  
  15.     [_backBtn addTarget:self action:@selector(backToRun:) forControlEvents:UIControlEventTouchUpInside];  
  16.     [self.view addSubview:_backBtn];  
  17. }  
  18. -(void)backToRun:(UIButton *)sender  
  19. {  
  20.     self.testBlock(_backBtn.titleLabel.text);  
  21.     [self.navigationController popViewControllerAnimated:YES];  
  22.       
  23. }  

最后得到的效果就是:第一个界面的按钮的title改变成了第二个界面的title的内容。。。。(也就是页面传值,在实际的项目中例如:有一个菜单是选择很多人的信息,我在这个菜单中选择了三个人,在点击保存的时候是需要将这三个人的数据返回到上个界面,此时应用Block操作简单快捷,例如这样的需求很多很多)

2.Block作为方法参数

在Block作为属性那个知识点中,仍然使用那两个controller去解释这块内容。

在ViewController中具体代码如下:



在RunViewController中具体代码如下:






最后得到的效果就是:点击当前页面的按钮,它直接得到的是另外一个类中传过来的数据(这个在实际应用开发中的例子一般是:封装一个解析类,往每个界面进行传输数据)

3.Block代替代理

同时使用Block和代理的情景如下:(请求框架中的内部实现)



之前一个项目中使用第一个版本请求使用的是代理模式,版本更新之后使用的是Block



这个书写方法如果页面请求过多,在ARC环境下,会造成delegate野指针崩溃的问题,所以在下个版本改进的时候使用block代替协议

[objc]  view plain  copy
  1. __weak __typeof(self)weakSelf = self;    
  2. AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {    
  3.     __strong __typeof(weakSelf)strongSelf = weakSelf;    
  4.     strongSelf.networkReachabilityStatus = status;    
  5.     if (strongSelf.networkReachabilityStatusBlock) {    
  6.         strongSelf.networkReachabilityStatusBlock(status);    
  7.     }    
  8. };    

Review一下上面这段代码,里面玄机不少。
第一行:__weak __typeof(self)weakSelf = self;
如之前第四条所说,为防止callback内部对self强引用,weak一下。
其中用到了__typeof(self),这里涉及几个知识点:
a. __typeof、__typeof__、typeof的区别
恩~~他们没有区别,但是这牵扯一段往事,在早期C语言中没有typeof这个关键字,__typeof、__typeof__是在C语言的扩展关键字的时候出现的。
typeof是现代GNU C++的关键字,从Objective-C的根源说,他其实来自于C语言,所以AFNetworking使用了继承自C的关键字。
b.对于老的LLVM编译器上面这句话会编译报错,所以在很早的ARC使用者中流行__typeof(&*self)这种写法,原因如下
大致说法是老LLVM编译器会将__typeof转义为 XXX类名 *const __strong的__strong和前面的__weak关键字对指针的修饰又冲突了,所以加上&*对指针的修饰。


第三行:__strong __typeof(weakSelf)strongSelf = weakSelf;
按照之前第五条的说法给转回strong了,这里__typeof()里面写的是weakSelf,里面写self也没有问题,因为typeof是编译时确定变量类型,所以这里写self 不会被循环引用。

第四、五、六行,如果不转成strongSelf而使用weakSelf,后面几句话中,有可能在第四句执行之后self的对象可能被析构掉,然后后面的StausBlock没有执行,导致逻辑错误。

最后第五行,使用前对block判空。


最后总结Block需要大家的注意点如下:

声明一个block变量一般是

[objc]  view plain  copy
  1. @property(nonatomic,strong)testBlock testBlock;  
block同时具有函数和实力便来给你的性质,使用起来方便,代码整洁。
使用注意事项:
1)在块内改变外部变量的值时候,在外部变量前加__block,否则该值在block块内部是只读的。
2)在引用某个实例变量或者所在控制器本身时候,在ARC下,要再前面加__weak如:__weak (typeof(self) weak self = self), 在mrc下用__block, 这样做是为了避免内存泄露和循环引用。

3)在使用block前需要对block指针做判空处理,如果是MRC的编译环境下,要先release掉block对象。

4)在MRC的编译环境下,block如果作为成员参数要copy一下将栈上的block拷贝到堆上(因为block默认是在栈上创建的,如果在定义block的作用于外部使用block那么需要使用copy将block放到堆上)//MRC下:_sucBlock = [callbackBlock copy]; 不copy block会在栈上被回收。

5)将block赋值为空,是解掉循环引用的重要方法。

6)还有一种改法,在block接口设计时,将可能需要的变量作为形参传到block中,从设计上解决循环引用的问题。

7)在多线程环境下(block中的weakSelf有可能被析构的情况下),需要先将self转为strong指针,避免在运行到某个关键步骤时self对象被析构。


Block基本的使用也就这么多了,以后遇到其他情况的时候大家一起共同来探讨,Block这个相当受欢迎哦!大家一定要记住Block的根本核心:“用它的回调方式实现你想要的效果”;希望大家在项目中能灵活运用。 奋斗

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值