咱们知道应用程序的内存分配有四个区:程序员
那么block做为OC对象是建立在哪一个区上呢?这篇文章咱们来探究一下。bash
block是一个NSBlock
对象,NSBLock
的声明以下:数据结构
@interface NSBlock : NSObject <NSCopying>
+ (id)alloc;
+ (id)allocWithZone:(struct _NSZone { }*)arg1;
- (id)copy;
- (id)copyWithZone:(struct _NSZone { }*)arg1;
- (void)invoke;
- (void)performAfterDelay:(double)arg1;
@end
复制代码
NSBlock
有三个子类,分别是:框架
接下来咱们分别研究一下这三种block。函数
_NSConcreteGlobalBlock
,顾名思义,全局block。如下两种状况初始化block时,生成的block为_NSConcreteGlobalBlock
:ui
代码以下:atom
/** 全局变量 */
int global_count = 10;
/** 静态全局变量 */
static int static_global_count = 10;
int main(int argc, const char * argv[]) {
/** 静态局部变量 */
static int static_count = 10;
void (^block)(void) = ^ {
global_count = 11;
static_global_count = 11;
static_count = 11;
};
block();
return 0;
}
复制代码
咱们打断点看一下,捕获捕获全局变量或者静态局部变量时,block为_NSConcreteGlobalBlock
,断点截图以下:spa
_NSConcreteStackBlock
为栈block,在ARC环境下,捕获局部变量、成员变量,且没有被强引用的block都是_NSConcreteStackBlock
。操作系统
OC代码以下:code
int main(int argc, const char * argv[]) {
/** 局部变量 */
int count = 10;
/** 执行 或者使用void (^__weak block)(void)来指向block */
^{
NSLog(@"%d", count);
}();
// 打印Block对象
NSLog(@"Block对象:%@", ^{
NSLog(@"%d", count);
});
return 0;
}
复制代码
控制台打印的信息以下:
Block对象:<__NSStackBlock__: 0x7ffeefbff568>
复制代码
OC代码以下:
@interface ViewController ()
@property (nonatomic, assign) int count;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"Block对象:%@", ^{
NSLog(@"%d", self.count);
});
^{
NSLog(@"%d", self.count);
}();
}
@end
复制代码
控制台打印的信息以下:
Block对象:<__NSStackBlock__: 0x7ffee554b838>
复制代码
总结来讲,block实现的内容依赖于执行时的状态,而且没有被强引用,且做用域仅为当前函数时,block为_NSConcreteStackBlock
,保存在栈上。
_NSConcreteMallocBlock
为堆block,为广域变量。在如下状况会被保存在堆上:
当block做为函数的返回值时,ARC环境下自动将block拷贝,扩大做用域,OC代码以下:
/** 函数建立 */
typedef void (^block)(void);
block function(int num) {
return ^{
NSLog(@"%d", num);
};
}
int main(int argc, const char * argv[]) {
// 函数的返回值赋值
void (^__weak block1)(void) = function(10);
// 打印block1对象
NSLog(@"block1对象:%@", block1);
return 0;
}
复制代码
控制台打印的结果以下:
block1对象:<__NSMallocBlock__: 0x103804d30>
复制代码
总结一下:由于block在函数内做为局部变量,若是不进行拷贝的话,函数返回时block就销毁了,因此ARC下系统帮咱们自动拷贝,MRC下直接编译报错,须要咱们手动拷贝block。
系统对于block做为参数时自动进行拷贝暂时没有时间证明,以后我会补上这部份内容。抱歉。。。
很少说,直接上表格:
Block的类 | 副本源的配置存储域 | 赋值效果 |
---|---|---|
_NSConcreteGlobalBlock | 数据区 | 什么也不作 |
_NSConcreteStackBlock | 栈 | 从栈拷贝到堆 |
_NSConcreteMallocBlock | 堆 | 引用计数增长 |
数据区中的_NSConcreteGlobalBlock
执行拷贝操做什么也不作的缘由是不须要,由于_NSConcreteGlobalBlock
在进程结束后才销毁,已是广域变量了。
判断block是何种类型的变量分如下几种状况:
_NSConcreteGlobalBlock
_NSConcreteStackBlock
_NSConcreteStackBlock
而且被强引用则为_NSConcreteMallocBlock