总结
block的本质就是Objective-C对象,block的调用就是函数指针的调用。
- block的本质是一个封装了函数调用以及函数调用环境的OC对象;
- block截获自动变量值的规则:
- 局部变量会被直接截获;
- 局部静态变量会被截获其指针;
- 全局变量并不会被截获,而是直接使用;
- block截获对象的规则:
- block位于栈上,则不会对截获的对象变量进行强引用;
- block从栈上复制到堆上,调用
copy
函数,对截获的变量进行强/弱引用; - block从堆上移除,调用
dispose
函数,自动释放引用的变量;
- block使用copy属性的原因:在MRC下,访问了自动变量的block处于栈上,容易被释放,使用copy可以将其复制到堆上,放在堆上便可以自己去控制Block的生命周期;在ARC下,对Block做了优化;
- block的循环引用:一个对象持有了这个Block,而在Block内部又使用了这个对象,就会造成循环引用;
- __block的作用:解决block内部无法修改自动变量值的问题;
- __block的注意点:不能修饰全局变量和静态变量;
- __block的实质:编译器将__block变量包装成一个对象,将截获到的值存在这个对象中,通过对截获的值进行赋值而更改原有的值;
- __forwarding指针的作用:可以实现无论__block变量配置在栈上还是堆上都能够正确地访问__block变量;
block有如下三种类型:
__NSGlobalBlock __ ( _NSConcreteGlobalBlock )
__NSStackBlock __ ( _NSConcreteStackBlock )
__NSMallocBlock __ ( _NSConcreteMallocBlock )
block在MRC和ARC环境下分别出现这三种类型的情况
MRC环境:
__NSGlobalBlock:没有访问外部auto变量,如果访问了外部static或者全局变量也是这种类型
void (^myBlock)(void) = ^{
NSLog(@"this is a __NSGlobalBlock__");
};
NSLog(@"%@",[myBlock class]);
__NSStackBlock:访问了外部auto变量
int age = 30;
void (^myBlock)(void) = ^{
NSLog(@"this is a __NSStackBlock__%d",age);
};
NSLog(@"this is a %@",[myBlock class]);
__NSMallocBlock:__NSStackBlock类型调用了copy方法
int age = 30;
void (^myBlock)(void) = [^{
NSLog(@"this is a __NSMallocBlock__%d",age);
} copy];
NSLog(@"this is a %@",[myBlock class]);
上面就是MRC环境下的三种类型,如果对__NSGlobalBlock和__NSMallocBlock分别调用copy会是什么情况呢?
__NSGlobalBlock调用copy还是__NSGlobalBlock类型,__NSMallocBlock调用copy则引用计数+1
再看下ARC环境下的三种类型:
__NSGlobalBlock:和MRC环境一样,不再累赘
__NSStackBlock:访问了外部变量,但没有强引用指向这个block,而是直接打印出来
int age = 30;
;
NSLog(@"this is a %@",[^{
NSLog(@"this is a __NSStackBlock__%d",age);
} class]);
__NSMallocBlock:ARC环境下只要访问了外部auto变量并且有强引用指向该block(或者作为函数返回值)就会自动将__NSStackBlock类型copy到堆上
这些情况有:
- block作为函数返回值的时候;
- block赋值给strong指针的时候;(arc下默认都是__strong修饰,所以下面的block赋值给了一个强引用指针)
- block作为Cocoa API中方法名含有usingBlock的方法参数的时候;
- block作为GCD API的参数的时候;
int age = 30;
void (^myBlock)(void) = ^{
NSLog(@"this is a __NSMallocBlock__%d",age);
};
NSLog(@"this is a %@",[myBlock class]);