Block的使用
1.概念:block实质就是一个匿名函数块,经过编译器的编译后,最终为一个函数,block也是objective-c中的对象,根据block对象创建时所处的数据区不同而进行区别:
在栈上创建的Block对象,_NSConcreteStackBlock
在堆上创建的Block对象,_NSConcreteMallocBlock
在全局数据区的Block对象,_NSConcreteGlobalBlock
block可以截取自动变量,所谓自动变量就是不是全局或者静态的变量,在当前上下文范围内,Block块可以捕捉到它的一个瞬间值,所谓瞬间值就是某一时刻的值,当我们在外面改变了变量的值,block内部仍然是之前某个瞬间的值,不会改变,譬如:
// 声明一个无返回值、无参数的block变量
__block int i = 10; // 如果block内部需要改变I的值,那么这里就应该给添加个__block,这时的变量成为__block变量
NSMutableArray *arr = [[NSMutableArray alloc] initWithObjects:@”1”,@”2”, nil];
NSLog(@”arr count:%ld”,arr.count);
void (^blockType)(void) = ^{
NSLog(@”In block,i = %d”,i); // 这里block截或了外部的自动变量,保存了”瞬间值”
i = 100;
NSLog(@”after change,i = %d”,i);
[arr removeObjectAtIndex:0];
};
i = 20;// 这里改变i的值,看下block中是否会打印这个i为20,虽然这里改变了i的值,但是外部的i是不会受到影响的
NSLog(@”out block,i = %d”,i);
blockType();
NSLog(@”arr count:%ld”,arr.count);
Block的存储区域:
void (^blk)(void) = ^ {
NSLog(@”Global Block”);
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
// BlockManager *manager = [[BlockManager alloc] init];
// [manager statementParameter];
//打印的是NSGlobalBlock
blk();
NSLog(@”%@”,[blk class]);
void (^blk1)(void) = ^ {
NSLog(@”Global Block”);
};
// 没有捕获了自动变量,打印的是NSGlobalBlock
blk1();
NSLog(@”%@”,[blk1 class]);
// 捕获了自动变量,打印的是NSMallocBlock
int i = 1;
void (^captureBlock)(void) = ^{
NSLog(@”Capture:%d”,i);
};
captureBlock();
NSLog(@”%@”,[captureBlock class]);
}
return 0;
}
Block复制:
配置在栈上的Block,如果其所属的栈作用域结束,该Block就会被废弃,对于超出Block作用域仍需使用Block的情况,Block提供了将Block从栈上复制到堆上的方法来解决这种问题,即便Block栈作用域已结束,但被拷贝到堆上的Block还可以继续存在。
在ARC有效时,大多数情况下编译器会进行判断,自动生成将Block从栈上复制到堆上的代码,以下几种情况栈上的Block会自动复制到堆上:
- 调用Block的copy方法
- 将Block作为函数返回值时
- 将Block赋值给__strong修改的变量时
- 向Cocoa框架含有usingBlock的方法或者GCD的API传递Block参数时
之前在栈上创建的block截取了自动变量i,但是实际却显示的是NSMallocBlock类,就是因为这个Block对象赋值给了_strong修饰的变量captureBlk(_strong是ARC下对象的默认修饰符)
因为上面四条规则,在ARC下其实很少见到_NSConcreteStackBlock类的Block,大多数情况编译器都保证了Block是在堆上创建的
3.使用__block发生了什么?
Block捕获的自动变量添加__block说明符,就可以在Block内读写该变量,也可以在原来的栈上读写该变量.
__block保证了栈上和Block内(通常在堆上)可以访问和修改“同一个变量”,__block是如何实现这一功能的?
__block发挥作用的原理:将栈上用__block修饰的自动变量封装成一个结构体,让其在堆上创建,以方便从栈上或堆上访问和修改同一份数据。