Block从简单到高级的使用,以及项目中经常使用的场景
Posted Dream-seekerGirl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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作为属性
@interface ViewController ()
///名字按钮
UIButton *_nameBtn;
@end
@implementation ViewController
- (void)viewDidLoad
[super viewDidLoad];
_nameBtn = [UIFactory buttonWithTitle:@"跑男" withRect:CGRectMake(50, 100, 100, 50) withBackgroundColor:[UIColor redColor]];
[_nameBtn addTarget:self action:@selector(run:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_nameBtn];
-(void)run:(UIButton *)sender
[self pushNextVCWithBlock:^(NSString *name)
[_nameBtn setTitle:name forState:UIControlStateNormal];
];
NSLog(@"第一次");
-(void)pushNextVCWithBlock:(testBlock)testBlock
RunViewController *runVC = [[RunViewController alloc]init];
//block作为属性传给下一个界面
runVC.testBlock = testBlock;
[self.navigationController pushViewController:runVC animated:YES];
@interface RunViewController : UIViewController
///Block作为属性<pre name="code" class="objc">@property(nonatomic,strong)testBlock testBlock;
</pre><pre name="code" class="objc"><pre name="code" class="objc">@end
#import "RunViewController.h"
@interface RunViewController ()
///返回按钮
UIButton * _backBtn;
@end
@implementation RunViewController
-(void)viewDidLoad
[super viewDidLoad];
_backBtn = [UIFactory buttonWithTitle:@"美女" withRect:CGRectMake(50, 100, 100, 50) withBackgroundColor:[UIColor redColor]];
[_backBtn addTarget:self action:@selector(backToRun:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_backBtn];
-(void)backToRun:(UIButton *)sender
self.testBlock(_backBtn.titleLabel.text);
[self.navigationController popViewControllerAnimated:YES];
最后得到的效果就是:第一个界面的按钮的title改变成了第二个界面的title的内容。。。。(也就是页面传值,在实际的项目中例如:有一个菜单是选择很多人的信息,我在这个菜单中选择了三个人,在点击保存的时候是需要将这三个人的数据返回到上个界面,此时应用Block操作简单快捷,例如这样的需求很多很多)
2.Block作为方法参数
在Block作为属性那个知识点中,仍然使用那两个controller去解释这块内容。
在ViewController中具体代码如下:
在RunViewController中具体代码如下:
最后得到的效果就是:点击当前页面的按钮,它直接得到的是另外一个类中传过来的数据(这个在实际应用开发中的例子一般是:封装一个解析类,往每个界面进行传输数据)
3.Block代替代理
同时使用Block和代理的情景如下:(请求框架中的内部实现)
之前一个项目中使用第一个版本请求使用的是代理模式,版本更新之后使用的是Block
这个书写方法如果页面请求过多,在ARC环境下,会造成delegate野指针崩溃的问题,所以在下个版本改进的时候使用block代替协议
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status)
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock)
strongSelf.networkReachabilityStatusBlock(status);
;
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变量一般是
@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的根本核心:“用它的回调方式实现你想要的效果”;希望大家在项目中能灵活运用。
以上是关于Block从简单到高级的使用,以及项目中经常使用的场景的主要内容,如果未能解决你的问题,请参考以下文章