iOS block之三种block

来源:互联网 发布:淘宝店卖肉需要执照吗 编辑:程序博客网 时间:2024/06/06 12:38

前言

首先引用《Objective-C高级编程》Blocks章节中的第一句话:Blocks是对C语言的扩充功能。而且OC是建立在C语言基础上之上,添加了面向对象机制的一门编程语言。

所以不要再说block的实现原理是C++的函数指针了,正确答案是:block的实现原理是C语言的函数指针。
函数指针即函数在内存中的地址,通过这个地址可以达到调用函数的目的。

Block是NSObject的子类,拥有NSObject的所有属性,所以block对象也有自己的生命周期,生存期间也会被持有和释放。


block有三种:

NSGlobalBlock 静态区(全局区)block,这是一种特殊的bloclk,因为不引用外部变量而存在。另外,作为静态区的对象,它的释放是有操作系统控制的,这一点我们最后再聊。
NSStackBlock 栈区block,位于内存的栈区,一般作为函数的参数出现。
NSMallocBlock 堆区block,位于内存的堆区,一般作为对象的property出现。

如果一个blcok引用了外部变量是栈block,则其不引用外部变量就成为了静态blcok。
如果一个block引用了外部变量是堆block,则其不引用外部变量就成为了静态block。


NSStackBlock 栈区block

说到栈操作,大家都明白,出栈和入栈,函数只有入栈后才能执行,出栈后就释放了。
栈block一般在函数内部定义,并在函数内部调用;或者在函数外部定义,作为函数的一个参数在函数内部调用。函数出栈时和其他变量或参数一起释放。

栈区block形式有2,如下:

栈区block形式1

- (void)xxx{    __block int i = 0;    void (^block1)() = ^{        //此处若不引用外部变量i,则block1是静态block,若引用,则为栈block        i++;        NSLog(@"i = %d",i);    };    block1();}

栈区block形式2:

- (void)saveFile:(NSDictionary *)dic complete:(void(^)(BOOL success))complete{    //dic写入本地近作demo使用,实际开发要判断路径是否存在,以及dic中是否存在null值等    NSString    * path = [NSHomeDirectory() stringByAppendingFormat:@"/Caches/dicInfo"];    BOOL suc = [dic writeToFile:path atomically:YES];    if (complete) {        //若complete中不引用外部变量suc,则complete是静态block,此处complete为栈block        complete (suc);    }}

NSMallocBlock 堆区block

堆区是内存的常驻区域,也叫永久存储区,block一般在函数中定义,最多是个栈block,什么时候才能打怪升级成为堆block呢?
在MRC时代你需要使用Block_copy()方法,才可以将blcok复制到堆中。

然而复制到堆中有何用处呢?
首先,作为一个对象,把它复制到堆中,想要使用它肯定要有一个指针指向它,而指向它的指针是作为property或静态变量出现的(如果不被引用也就没有了常驻于堆区的意义),而实际开发中多使用rpoperty引用。

在MRC中,如果一个类的block属性是使用copy修饰的,则不需要手动调用Block_copy将其复制到堆中。如果是用strong修饰的,则必须使用Block_copy()将其复制到堆中,并在释放时调用Block_release()方法将其释放,否则会因野指针导致程序崩溃。

@interface TestV : UIView@property (nonatomic, strong) void(^block1)();@property (nonatomic, copy) void(^block2)();@end
- (void)testFunc{    __block int i = 0;    void (^block1)() = ^{        //此处若不引用外部变量i,则block1是静态block,若引用,则为堆block        i++;        NSLog(@"block1 -- i = %d",i);    };    void (^block2)() = ^{        i --;        NSLog(@"block2 -- i = %d",i);    };    TestV * view = [[TestV alloc] initWithFrame:CGRectMake(100, 100, kScreenWidth - 200, 40)];    [self.view addSubview:view];    view.block1 = Block_copy(block1);    view.block2 = block2;    NSLog(@"block1 -- %@",block1);    NSLog(@"block2 -- %@",block2);}

因此在MRC中,基本上会用copy关键字修饰block,既优雅又省代码,还避免了因为未调用Block_release而造成内存泄露,这大概是很多人在ARC项目中依旧使用copy修饰block的历史原因。

其实在ARC下使用strong或copy修饰block没啥区别,亲测有效,可能会遇到一些开发人员,他们看到ARC工程中出现strong修饰的block就会提出质疑,那只能说明他们没有亲自写两行代码测试过是否真的有区别。


NSGlobalBlock 静态block

这货之所以放在最后再聊,主要是这家伙的释放有两种不同的时机。
1、如果这个block引用了外部变量后是栈block,则在定义此block的函数出栈时,block释放。
2、如果这个blcok引用了外部变量之后是堆block,则其宿主target释放的时候此block才释放。


下一篇博文《block之循环引用》,我们将会讲block与循环引用。

以上观点均为个人研究、理解,如有不妥之处,欢迎指正,谢谢!

原创粉丝点击