ARC和MRC下的block的内存分布详解

来源:互联网 发布:晨曦软件视频教程 编辑:程序博客网 时间:2024/05/17 07:39
 Block有三种类型,分别是

 __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(nonatomicweakvoid(^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(nonatomicweakvoid(^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

原创粉丝点击