前言
Block的文章看了很多,但是都不得要领。最近看了霜神的文章(https://juejin.im/post/57ccab0ba22b9d006ba26de1),然后又重读了一遍《Objective-C高级编程》,根据文章和书中的内容,用clang把OC转成C,然后才懂了很多原来似是而非的问题。
欢迎讨论,更欢迎大佬指点。
怎么才能真正了解Block
1.知道clang的几个指令
2.有C的基础
3.亲自动手去试
需要用到的clang指令与操作 我是先转c文件,看的差不多了,然后转oc文件。循序渐进。
0.cd 进入目标文件的文件夹 clang -rewrite-objc xxxx.c 在文件夹下找.cpp文件 建议先用c文件来尝试。
1.cd 进入目标文件的文件夹clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk xxx.m xxx是OC文件的名。
先创建一个c文件 写如下代码
int test(void){ void (^studyBlock)(void) = ^{ printf("这是 studyBlock");};studyBlock();}复制代码
**通过clang -rewrite-objc xxxx.c 得到了一个接近600行的cpp文件。很多代码是与Block无关的。相关代码如下:请注意区分大小写。
//Block 结构体 相当于Foundation框架中的NSObject对象,因为有isa指针,所以Block也可看做对象的一种。;struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr;};//这是我们声明的block( void(^block)(void) ),这是一个block声明,转换完之后代码有这么多。。struct __test_block_impl_0 { struct __block_impl impl; struct __test_block_desc_0* Desc; __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};//studyBlock体中的内容^{ printf("这是 studyBlock"); };static void __test_block_func_0(struct __test_block_impl_0 *__cself) { printf("这是 studyBlock");}//每个Block转化之后都有这样的结构体,用于记录版本和大小static struct __test_block_desc_0 { size_t reserved; size_t Block_size;} __test_block_desc_0_DATA = { 0, sizeof(struct __test_block_impl_0)};int test(void){ //studyBlock调用 studyBlock(); void (*studyBlock)(void) = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA)); ((void (*)(__block_impl *))((__block_impl *)studyBlock)->FuncPtr)((__block_impl *)studyBlock); return 0;}复制代码
好了现在知道了 Block的真面目了。下面加入一些变量。代码如下。
//加入了全局变量 全局静态变量 局部静态变量 __block变量 局部变量(自动变量)int globelVal = 0;static int staticGlobelVal = 1;int test(void){ static int staticVal = 2; __block int blockVal = 3; int val = 4; void (^studyBlock)(void) = ^{ printf("%d===%d===%d===%d===%d",globelVal,staticGlobelVal,staticVal,blockVal,val); }; studyBlock(); return 0;}复制代码
代码很少 也很好理解,下面我们转换一下
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; int globelVal = 0; static int staticGlobelVal = 1; //__block类型的变量 //结构体中的 isa指针,指向原变量isa,用于保存变量类型。 //__Block_byref_blockVal_0 。__Block_byref_blockVal_0结构中的__Block_byref_blockVal_0。为什么要有这样的结构呢。是因为Block有可能从栈中复制到堆中。 //当Block从栈中复制到堆中,结构体中的__forwarding指向栈中的__Block_byref_blockVal_0结构体。然后通过__forwarding->blockVal来操作变量的值。 //如果没有复制到栈中的操作。__forwarding指向栈中的__Block_byref_blockVal_0结构体。 struct __Block_byref_blockVal_0 { void *__isa; __Block_byref_blockVal_0 *__forwarding; int __flags; int __size; int blockVal; }; struct __test_block_impl_0 { struct __block_impl impl; struct __test_block_desc_0* Desc; //我们可以看到静态局部变量和局部变量的区别了。 //静态局部变量被引入studyBlock结构体的时候,是通过指针的方式引入的。通过指针可以修改内存中该变量的值。那么再看局部变量,只是一个值被引入了,所以不能修改局部变量的值。 //__Block_byref_blockVal_0 *blockVal;这个是__blockVal; //被引入studyBlock结构体之后,变成了一个__Block_byref_blockVal_0型的结构体变量。 int *staticVal; int val; __Block_byref_blockVal_0 *blockVal; // by ref __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int *_staticVal, int _val, __Block_byref_blockVal_0 *_blockVal, int flags=0) : staticVal(_staticVal), val(_val), blockVal(_blockVal->__forwarding) { impl.isa = &_NSConcreteStackBlock; mpl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void __test_block_func_0(struct __test_block_impl_0 *__cself) { __Block_byref_blockVal_0 *blockVal = __cself->blockVal; // bound by ref int *staticVal = __cself->staticVal; // bound by copy int val = __cself->val; // bound by copy printf("%d===%d===%d===%d===%d",globelVal,staticGlobelVal,(*staticVal),(blockVal->__forwarding->blockVal),val);} static void __test_block_copy_0(struct __test_block_impl_0*dst, struct __test_block_impl_0*src) {_Block_object_assign((void*)&dst->blockVal, (void*)src->blockVal, 8/*BLOCK_FIELD_IS_BYREF*/);} static void __test_block_dispose_0(struct __test_block_impl_0*src) {_Block_object_dispose((void*)src->blockVal, 8/*BLOCK_FIELD_IS_BYREF*/);} static struct __test_block_desc_0 { size_t reserved; size_t Block_size; void (*copy)(struct __test_block_impl_0*, struct __test_block_impl_0*); void (*dispose)(struct __test_block_impl_0*); } __test_block_desc_0_DATA = { 0, sizeof(struct __test_block_impl_0), __test_block_copy_0, __test_block_dispose_0}; int test(void){ static int staticVal = 2; __attribute__((__blocks__(byref))) __Block_byref_blockVal_0 blockVal = {(void*)0,(__Block_byref_blockVal_0 *)&blockVal, 0, sizeof(__Block_byref_blockVal_0), 3}; int val = 4; void (*studyBlock)(void) = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA, &staticVal, val, (__Block_byref_blockVal_0 *)&blockVal, 570425344)); ((void (*)(__block_impl *))((__block_impl *)studyBlock)->FuncPtr)((__block_impl *)studyBlock);return 0;}复制代码
补充 Block的isa指针分为三种类型。_NSConcreteStackBlock栈。_NSConcreteGlobalBlock数据区。_NSConcreteMallocBlock堆。
课后题
1.为什么普通变量不能再Block中修改。
2.Block分为几种类型
3.__block类型为什么能够在Block中修改值。