前言
Block的文章看了很多,但是都不得要领。最近看了霜神的文章(https://juejin.cn/post/6844903443463667726),然后又重读了一遍《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中修改值。