大家好,我是OB!今天来聊聊大家的老熟人Block!
block 本质就是NSObject对象,把方法包装成了block块
来看看block的真面目
void(^OBblock)(void) = ^{ NSLog(@"-------"); }; OBblock(); /* * 编译后 */ void(*OBblock)(void) = &__main_block_impl_0( __main_block_func_0, &__main_block_desc_0_DATA ); OBblock->FuncPtr(OBblock);编译后
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; //block 最终变成了 __main_block_impl_0类型的结构体 struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; //构造函数,返回 __main_block_impl_0 类型的实例 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; //block块中的实现,放在这个函数中 static void __main_block_func_0(struct __main_block_impl_0 *__cself) { NSLog((NSString *)&__NSConstantStringImpl__var_fo_T_main_0072ab_mi_0); }值捕获
//全局变量 int height_ = 20; static int weight_ = 20; void test() { auto int num = 20; static int age = 20; void(^OBblock)(void) = ^{ NSLog(@"num:%d age:%d %d %d",num,age,height_,weight_); }; num = 10; age = 10; OBblock(); }再看看结构体发生变化没
void(*OBblock)(void) = &__main_block_impl_0( __main_block_func_0, &__main_block_desc_0_DATA, num, //值捕获,20已经被赋值到结构体中了 &age //指针捕获 ); //全局变量height_,weight_没有捕获 struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int num; int *age; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _num, int *_age, int flags=0) : num(_num), age(_age) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };多了num *age构造函数也多了一个: num(_num) age(_age)(将_nun的值自动赋值给num变量),但是 全局变量height_,weight_没有捕获却没有捕获全局变量,
调用函数实现:num直接取值__cself->num,static变量:__cself->age指针传递,获取age地址取值,全局变量height_,weight_直接访问
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int num = __cself->num; // bound by copy int *age = __cself->age; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__..._mi_0,num,(*age),height_,weight_); }捕获与否取决去block的函数执行块,执行时,能不能访问改变量。
变量类型block捕获访问方式能否修改局部变量:auto捕获值传递添加__block 修饰局部变量:static捕获指针传递可直接修改全局变量:static/auto不捕获直接访问可直接修改block 本质是一个结构体,换成OC那就是block本质是NSObject;
void getType(id obj) { if (obj == NULL) { return; } Class currentClass = [obj class]; NSLog(@"%@",currentClass); getType([(NSObject*)currentClass superclass]); }可以看到结果:block 也是继承 NSObject
Test_05_block[56306:5305484] __NSGlobalBlock__ Test_05_block[56306:5305484] __NSGlobalBlock Test_05_block[56306:5305484] NSBlock Test_05_block[56306:5305484] NSObject进一步探索发现:block的三种类型
block访问的变量类型block类型copy操作没有访问局部auto变量__NSGlobalBlock__一直在数据区什么也不做,反正也没有访问变量,一般不考虑访问局部auto变量__NSStackBlock__从栈区copy到了堆区,StackBlock变成了MallocBlockNSStackBlock调用了copy__NSMallocBlock__引用计数加一注意:NSStackBlock调用了copy变成__NSMallocBlock__其实就是对象的生命周期或者是变量的生命周期不统一,需要放到一个能长久访问的地方,防止程序访问时访问不到值。所以ARC才会自动给NSStackBlock copy一下,变成__NSMallocBlock__
使用block会造成block捕获对象,会造成对象的引用计数+1,这时就会可能造成循环引用
解决循环引用方法操作安全性__weak指针指向的对象销毁时,自动置为nil安全__unsafe_unretained指针指向的对象销毁时,还指向那个地址不安全__block利用在block中可以修改对象的特性,将obj=nil安全不使用 __weak
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; Animal *animal; //对象引用计数+1 ... };使用 __weak后
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; Animal *__weak animal; //变成弱引用 ... };