__NSGlobalBlock__:全局Block,程序被加载后被分配在进程数据段上(类似函数,位于text段),也就是常量,静态创建的Block。
__NSMallocBlock__:在进程堆上分配的Block,动态创建的Block。
__NSStackBlock__:进程栈上分配的Block,动态创建的Block。
在ARC下我一直以为__NSStackBlock__已经不会出现的了,事实上也并非如此,我们可以强行让它出现,但实际开发中应该不会做这么无聊的事。
先看看一个DEMO。
——————————————————
- (void)viewDidLoad {
[superviewDidLoad];
void(^blockA)() = ^{
NSLog(@"just a block");
};
NSLog(@"%@", blockA);
int value = 10;
void(^blockB)() = ^{
NSLog(@"just a block === %d", value);
};
NSLog(@"%@", blockB);
void(^ __weak blockC)() = ^{
NSLog(@"just a block === %d", value);
};
NSLog(@"%@", blockC);
}
——————————————————
三个NSLog打印的内容为:
<__NSGlobalBlock__: 0x105a730d0>
<__NSMallocBlock__: 0x600000052c00>
<__NSStackBlock__: 0x7fff5a18b7e0>
注意看它们的地址,__NSGlobalBlock__的地址明显要短,因为它是在进程数据段上的。
blockC则是强行用__weak声明让其分配在栈上,这里会看到一个黄色的警告,大意就是指分配后就会被释放。就是说viewDidLoad这个方法return后这个block就会被释放。
动态分配和静态分配的区分是在哪里?观察一下就发现__NSGlobalBlock__类型是没有捕获局部变量的,它只是打印一一个字符串。通过NSString literal创建的字符串是放在常量区的,也就是数据段上。全局的block里没有引用任何堆或栈上的数据。另外如果将上面的例子中的int value = 10;改为const int value = 10;那么blockB将变成__NSGlobalBlock__,这是因为const修饰下value里的值会存储在常量区即数据段上,也就是不违反原则,只要block literal里没有引用栈或堆上的数据,那么这个block会自动变为__NSGlobalBlock__类型,这是编译器的优化。
在属性声明上,我们一般会用copy修饰一个Block属性。原因是什么?
在MRR或MRC(两个词都是指同一个玩意)中,block默认是在栈上创建的。如果我们将它赋值给一个成员变量,如果成员变量没有被copy修饰或在赋值的时候没有进行copy,那么在使用这个block成员变量的时候就会崩溃。因为栈上的出了作用域就被释放掉了,copy操作可以将block从栈上copy到堆上
很早的时候MRC的block属性都是在栈区的,copy之后就到堆区了
当前的ARC的block属性默认都在堆区,使用copy知识沿袭了历史的习惯,使用strong也是没有问题的
思考一下下面的代码。
————————————————————————————————————
@property(nonatomic, weak) void(^block)();
- (void)viewDidLoad {
[superviewDidLoad];
int value = 10;
void(^blockC)() = ^{
NSLog(@"just a block === %d", value);
};
NSLog(@"%@", blockC);
_block = blockC;
}
- (IBAction)action:(id)sender {
NSLog(@"%@", _block);
}
这代码在MRC下是会崩溃的。但ARC下就不会了,因为block默认就创建在堆上了。但是不是意味着ARC不用写copy来修饰block属性呢?当然不是了,上面已经说了,我们是可以强行在ARC上将一个block创建在栈上的。
int value = 10;
void(^ __weak blockC)() = ^{
NSLog(@"just a block === %d", value);
};
————————————————————————————————————
最后留一个思考题,下面这种情况会不会崩毁?为什么?
@property(nonatomic, weak) void(^block)();
- (void)viewDidLoad {
[superviewDidLoad];
void(^ __weak blockA)() = ^{
NSLog(@"just a block");
};
_block = blockA;
}
- (IBAction)action:(id)sender {
_block();
}
上面的这个也是不会crash的,因为没有引用临时(内部或者外部变量),是个__NSGlobalBlock__类型的block,程序运行期间都不会释放,所以根本就不用担心野指针问题喽!
参考文章:
http://www.cocoachina.com/bbs/read.php?tid=1713528
http://www.cnblogs.com/Kelphizy/p/4536500.html
http://blog.csdn.net/sinat_20559947/article/details/52610467