Block深入学习,授人以渔。—— Block与各种变量

340 阅读4分钟

前言

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中修改值。

下一节 探究weakSelf,strongSelf。