栈块、堆块、全局块 (Block详解)
对于Block之前只是在用,对于栈,堆这块没有细入研究,今天抽空把”Effectiv Objective-c 2.0”这本书看了一下,下面是一些概念及实例。
分几类
栈Block
堆Block
全局Block
block的存储形态有三种:_NSConcretStackBlock(栈)、_NSConcretGlobalBlock(全局)、_NSConcretMallocBlock(堆)
要点一:当block在函数内部,且定义的时候就使用了函数内部的变量,那么这个 block是存储在栈上的。
要点二:当block定义在函数体外面,或者定义在函数体内部且当时函数执行的时候,block体中并没有需要使用函数内部的局部变量时,也就是block在函数执行的时候只是静静地待在一边定义了一下而不使用函数体的内容,那么block将会被编译器存储为全局block。
要点三:全局block存储在全局区中,对全局block使用copy操作会返回原函数指针;而对栈中的block使用copy操作,会产生两个不同的block地址,也就是两个匿名函数的入口地址。
isa = &_NSConcreteGlobalBlock对象的Block, 会存储在数据域, 因为它创建在全局域, 因此不会截获自动变量, 全局只需要一个实例即可, 因此将该Block存储在数据域. 有以下两种情况创建的Block是_NSConcreteGlobalBlock:
记述全局变量的地方有Block语法时
Block语法的表达式中不使用截获的自动变量时候(只截获静态变量, 或者不截获变量)
1.为什么block中无法修改非静态局部变量呢?
第一反应是变量是值传递到block中的,故无法修改。为什么对待非静态局部变量不能像对待静态局部变量那样,直接用指针传递呢?说到这就不得不说,静态局部变量和非静态局部变量的区别了,静态变量存在于应用程序的整个生命周期,而非静态局部变量,仅仅是存在于一个局部的上下文中。如果block执行过程中其所指向的非静态局部变量还没有被栈回收的话,这样执行是ok,然后绝大多数情况下,block都是延后执行的,故这样非常不妥。
在 ARC 中,捕获外部了变量的 block 的类会是 __NSMallocBlock__ 或者 __NSStackBlock__,如果 block 被赋值给了某个变量在这个过程中会执行 _Block_copy 将原有的 __NSStackBlock__ 变成 __NSMallocBlock__;但是如果 block 没有被赋值给某个变量,那它的类型就是 __NSStackBlock__;没有捕获外部变量的 block 的类会是 __NSGlobalBlock__ 即不在堆上,也不在栈上,它类似 C 语言函数一样会在代码段中。
在非 ARC 中,捕获了外部变量的 block 的类会是 __NSStackBlock__,放置在栈上,没有捕获外部变量的 block 时与 ARC 环境下情况相同。
对于对象类型的局部变量,block内部会连同截获所有权修饰符一起截获。
综上,可以知道,为什么block会对内部对象进行强引用。(因此使用__weak可以解决)
什么情况下栈上的block会隐式地进行copy操作。
block被赋值到堆上的block变量
在ARC环境下,block被赋值给__strong属性标记的block变量
在ARC环境下,block被当作返回block时
2019-02-08 2019-02-08 2019-02-08 2019-02-08这种解决循环引用的方法有一个弊端,那就是,如果不调用这个block,循环引用的环就会一直存在。
ARC下Block何时会从栈自动被复制到推, 以及__block和__weak的使用问题
由于Block是默认建立在栈上, 所以如果离开方法作用域, Block就会被丢弃, 在非ARC情况下, 我们要返回一个Block ,需要 [Block copy];
在ARC下, 以下几种情况, Block会自动被从栈复制到堆:
1.被执行copy方法
2.作为方法返回值
3.将Block赋值给附有__strong修饰符的id类型的类或者Blcok类型成员变量时
4.在方法名中含有usingBlock的Cocoa框架方法或者GDC的API中传递的时候.
对于非ARC下, 为了防止循环引用, 我们使用__block来修饰在Block中实用的对象:
__block id blockSelf=self;
self.blk=^{
NSLog(@"%@",blockSelf); //在非ARC下对于栈上的_block对象, Block不会对其复制, 仅仅使用, 不会增加引用计数.
};
对于ARC下, 为了防止循环引用, 我们使用__weak来修饰在Block中实用的对象:
__weak id weakSelf=self;
self.blk=^{
NSLog(@"%@",weakSelf);
};
如果要在ARC下, 为了防止循环引用, 使用__block来修饰在Block中实用的对象,仍然会被retain, 所以需要多做一些设置
__block id blockSelf=self;
self.blk=^{
NSLog(@"%@",blockSelf);
blockSelf=nil;
};
并且一定要运行一次blk();
这样就使blk断开了与blockSelf的持有关系, 这是使用__block是为了允许在blk修改其值.
这么多好处是可以自己控制对self的持有时间.
网友评论