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的根本核心:“用它的回调方式实现你想要的效果”;希望大家在项目中能灵活运用。奋斗






















阅读更多
换一批

没有更多推荐了,返回首页