iOS Block底层实现原理详解
2016-04-28 14:57
1281 查看
block原始代码为:
所谓结构体类型的自动变量,即栈上生成的该结构体的实例。
存储域
通过上面的描述我们知道Block和__block变量实质就是一个相应结构体的实例。我们在上述转换过的代码中可以发现 __main_block_impl_0 结构体构造函数中, isa指向的是 _NSConcreteStackBlock。Block还有另外两个与之相似的类:
_NSConcreteStackBlock 保存在栈中的block,出栈时会被销毁
_NSConcreteGlobalBlock 全局的静态block,不会访问任何外部变量
_NSConcreteMallocBlock 保存在堆中的block,当引用计数为0时会被销毁
上述示例代码中,Block是被设为_NSConcreteStackBlock,在栈上生成。当我们把Block作为全局变量使用时,对应生成的Block将被设为_NSConcreteGlobalBlock,如:
该代码转换后的代码中,Block结构体的成员变量isa的初始化如下:
那么_NSConcreteMallocBlock在什么时候被使用呢?
分配在全局变量上的Block,在变量作用域外也可以通过指针安全的访问。但分配在栈上的Block,如果它所属的变量作用域结束,该Block就被废弃。同样地,__block变量也分配在栈上,当超过该变量的作用域时,该__block变量也会被废弃。
这个时候_NSConcreteMallocBlock就登场了,Blocks提供了将Block和__block变量从栈上复制到堆上的方法来解决这个问题。将分配到栈上的Block复制到堆上,这样但栈上的Block超过它原本作用域时,堆上的Block还可以继续存在。
复制到堆上的Block,它的结构体成员变量isa将变为:
而_block变量中结构体成员__forwarding就在此时保证了从栈上复制到堆上能够正确访问__block变量。在这种情况下,只要栈上的_block变量的成员变量__forwarding指向堆上的实例,我们就能够正确访问。
我们一般可以使用copy方法手动将 Block 或者 __block变量从栈复制到堆上。比如我们把Block做为类的属性访问时,我们一般把该属性设为copy。有些情况下我们可以不用手动复制,比如Cocoa框架中使用含有usingBlock方法名的方法时,或者GCD的API中传递Block时。
当一个Block被复制到堆上时,与之相关的__block变量也会被复制到堆上,此时堆上的Block持有相应堆上的__block变量。当堆上的__block变量没有持有者时,它才会被废弃。(这里的思考方式和objc引用计数内存管理完全相同。)
block属性
这里还有一点关于block类型的ARC属性。上文也说明了,ARC会自动帮
int main(int argc, const char * argv[]) { @autoreleasepool { int a = 10; void (^block)() = ^{ printf("%d\n", a); }; //再次给a 赋值 a = 20; block(); } return 0; }将上面block原始代码转换为C++代码: 通过 命令 :clang -rewrite-objc main.m
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int a; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int a = __cself->a; // bound by copy printf("%d\n", a); } static struct __main_block_desc_0 { size_t reserved; size_t Block_size; } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)}; int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; int a = 10; void (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a)); a = 20; ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); } return 0; } static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };block原始代码为:
int main(int argc, const char * argv[]) { @autoreleasepool { __block int a = 10; void (^block)() = ^{ printf("%d\n", a); }; //再次给a 赋值 a = 20; block(); } return 0; }将上面block原始代码转换为C++代码: 通过 命令 :clang -rewrite-objc main.m
struct __Block_byref_a_0 { void *__isa; __Block_byref_a_0 *__forwarding; int __flags; int __size; int a; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_a_0 *a; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { __Block_byref_a_0 *a = __cself->a; // bound by ref printf("%d\n", (a->__forwarding->a)); } static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);} static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);} static struct __main_block_desc_0 { size_t reserved; size_t Block_size; void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*); void (*dispose)(struct __main_block_impl_0*); } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0}; int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10}; void (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));//这里的<span style="font-family: Arial, Helvetica, sans-serif;">570425344 最后赋值给 </span><span style="font-family: Arial, Helvetica, sans-serif;">flags 但是 flags 直接又被赋为 0,这个</span><span style="font-family: Arial, Helvetica, sans-serif;">570425344在此处没用。</span><span style="font-family: Arial, Helvetica, sans-serif;"> </span><span style="font-family: Arial, Helvetica, sans-serif;"> </span> (a.__forwarding->a) = 20; ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); } return 0; } static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
所谓结构体类型的自动变量,即栈上生成的该结构体的实例。
类 | 设置对象的存储域 |
_NSConcreteStackBlock | 栈 |
_NSConcreteGlobalBlock | 程序的代码段(.text区) |
_NSConcreteMallocBlock | 堆 |
通过上面的描述我们知道Block和__block变量实质就是一个相应结构体的实例。我们在上述转换过的代码中可以发现 __main_block_impl_0 结构体构造函数中, isa指向的是 _NSConcreteStackBlock。Block还有另外两个与之相似的类:
_NSConcreteStackBlock 保存在栈中的block,出栈时会被销毁
_NSConcreteGlobalBlock 全局的静态block,不会访问任何外部变量
_NSConcreteMallocBlock 保存在堆中的block,当引用计数为0时会被销毁
上述示例代码中,Block是被设为_NSConcreteStackBlock,在栈上生成。当我们把Block作为全局变量使用时,对应生成的Block将被设为_NSConcreteGlobalBlock,如:
分配在全局变量上的Block,在变量作用域外也可以通过指针安全的访问。但分配在栈上的Block,如果它所属的变量作用域结束,该Block就被废弃。同样地,__block变量也分配在栈上,当超过该变量的作用域时,该__block变量也会被废弃。
这个时候_NSConcreteMallocBlock就登场了,Blocks提供了将Block和__block变量从栈上复制到堆上的方法来解决这个问题。将分配到栈上的Block复制到堆上,这样但栈上的Block超过它原本作用域时,堆上的Block还可以继续存在。
复制到堆上的Block,它的结构体成员变量isa将变为:
我们一般可以使用copy方法手动将 Block 或者 __block变量从栈复制到堆上。比如我们把Block做为类的属性访问时,我们一般把该属性设为copy。有些情况下我们可以不用手动复制,比如Cocoa框架中使用含有usingBlock方法名的方法时,或者GCD的API中传递Block时。
当一个Block被复制到堆上时,与之相关的__block变量也会被复制到堆上,此时堆上的Block持有相应堆上的__block变量。当堆上的__block变量没有持有者时,它才会被废弃。(这里的思考方式和objc引用计数内存管理完全相同。)
// // main.m // test_block_05 // // Created by admin on 4/28/16. // Copyright © 2016 jeffasd. All rights reserved. // #import <Foundation/Foundation.h> void (^globalBlock)() = ^(){ NSLog(@"fsdfsdf"); return; }; int main(int argc, const char * argv[]) { @autoreleasepool { int foo = 3; NSLog(@"the foo address is %p", &foo); int foo1 = 4; NSLog(@"the foo1 address is %p", &foo1); int foo2 = 5; NSLog(@"the foo2 address is %p", &foo2); __block int a = 10; // int a = 10; NSLog(@"11 %p", &a); //0x7fff5fbff7f8 地址很大在栈中 void (^block)() = ^{ NSLog(@"block a address %p", &a); //0x7fff5fbff7f8 地址很小在堆中 printf("%d\n", a); }; //再次给a 赋值 NSLog(@"11 block is %@", block); NSLog(@"%p", &a); a = 20; block(); NSLog(@"block is %@", block); NSLog(@"the global block is %@", globalBlock); globalBlock(); NSLog(@"the global block is %@", globalBlock); __weak void (^weakBlock)() = ^(){ NSLog(@"fsdfsdf"); return; }; NSLog(@"the weak block is %@", weakBlock); } return 0; } //int main(int argc, const char * argv[]) { // /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; // // int a = 10; // // void (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a)); // // // // a = 20; // // ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); // } // return 0; //} //static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 }; //切换成C++语音 //int main(int argc, const char * argv[]) { // @autoreleasepool { // // __block int a = 10; // // void (^block)() = ^{ // printf("%d\n", a); // }; // // //再次给a 赋值 // // a = 20; // // block(); // } // return 0; //} ////切换成C++语音 //int main(int argc, const char * argv[]) { // /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; // // __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10}; // // void (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344)); // // // // (a.__forwarding->a) = 20; // // ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); // } // return 0; //} //static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
block属性
这里还有一点关于block类型的ARC属性。上文也说明了,ARC会自动帮
strong类型且
捕获外部变量的block进行copy,所以在定义block类型的属性时也可以使用strong,不一定使用copy。也就是以下代码:
/** 假如有栈block赋给以下两个属性 **/ // 这里因为ARC,当栈block中会捕获外部变量时,这个block会被copy进堆中 // 如果没有捕获外部变量,这个block会变为全局类型 // 不管怎么样,它都脱离了栈生命周期的约束 @property (strong, nonatomic) Block *strongBlock; // 这里都会被copy进堆中 @property (copy, nonatomic) Block *copyBlock;
相关文章推荐
- iOS图片自由拉伸
- iOS NSOutputStream向输出流写数据
- IOS开发源码阅读篇--FMDB源码分析2(FMDatabase+FMDatabaseAdditions)
- IOS开发源码阅读篇--FMDB源码分析1(FMResultSet)
- iOS KVC解析Json数据
- ios cell左滑删除
- 【iOS】NSNumberFormatter
- iOS开发之高效绘图,通过一个绘图应用进行讲解
- iOS循环引用问题集合
- iOS动画详解
- IOS开发之—— Core Foundation对象与OC对象相对转换的问题
- iOS中通知中心(NSNotificationCenter)的使用总结
- ios 单击和双击手势
- iOS地图集成示例:百度地图POI检索
- ios开发 文件操作
- 准备要开始学习IOS系统编程了
- 常见的iOS对动画的操作
- iosGCD基本用法
- iOS collectionView的使用
- iOS presentViewController两个视图控制器的时候返回根视图