iOS开发iOS学习集--网上转载iOS源码探究相关

那些关于iOS Blocks的坑(一)

2016-06-10  本文已影响777人  shawenlx


这个专题主要介绍Blocks的底层实现,分为以下几个部分,将通过多篇博文一一阐述。
1.Blocks的本质
2.Blocks为什么截取自动变量值
3.__block说明符
4.关于Blocks的存储
5.关于__block变量的存储
6.Blocks的循环引用问题


Blocks的本质

//block.m
int main() {
    int count = 10;
    void (^blk)(void) = ^{
        printf("%d\n", count);
    };
    blk();
    return 0;
}

<strong>clang -rewrite-objc 源代码文件名</strong>

#define BLOCK_IMPL
struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    int count;

  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _count, int flags=0) : count(_count) {
      impl.isa = &_NSConcreteStackBlock;
      impl.Flags = flags;
      impl.FuncPtr = fp;
      Desc = desc;
  }
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    int count = __cself->count; // bound by copy
    printf("%d\\n", count);
}

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 count = 10;
    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, count));
    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    return 0;
}

以上便是那份OC代码block.m文件编译出来摘取的关键代码。几行代码瞬间变成了几十行代码。看着很吓人,其实不难分析。

先来看main函数中的调用声明和调用block的代码,转化成了什么。
int main() {
    int count = 10;
    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, count));
    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    return 0;
}

__cself参数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    int count = __cself->count; // bound by copy
    printf("%d\\n", count);
}
struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    int count;
}

结构体中的第一个参数所对应的结构体 struct __block_impl,根据名称可以联想到某些标志,今后版本升级所需的区域,以及函数指针。

struct __block_impl {
    void *isa;  
    int Flags;        //标志
    int Reserved;     //今后版本升级所需的区域
    void *FuncPtr;    //函数指针
};
static struct __main_block_desc_0 {
    size_t reserved;      //今后版本升级所需的区域
    size_t Block_size;    //Block的大小
} 
__main_block_func_0函数
int count = __cself->count; // bound by copy
printf("%d\\n", count);

Blocks为什么截取自动变量值

何为值拷贝,何为地址拷贝?

__block关键字

//block.m
int main() {
    int count = 10;
    void (^blk)(void) = ^{
        count = 11;//编译错误
    };
    blk();
    return 0;
}
error: variable is not assignable (missing __block type specifier)
        count = 11;
解决方案

//block.m
int main() {
   __block int count = 10; 
   void (^blk)(void) = ^{ 
      count = 11;
   };
   blk();
   return 0;
}

clang -rewrite-obj 编译文件

struct __Block_byref_count_0 {
  void *__isa;
__Block_byref_count_0 *__forwarding;
 int __flags;
 int __size;
 int count;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_count_0 *count; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_count_0 *_count, int flags=0) : count(_count->__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_count_0 *count = __cself->count; // bound by ref

        (count->__forwarding->count) = 11;
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->count, (void*)src->count, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->count, 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() {

    __attribute__((__blocks__(byref))) __Block_byref_count_0 count = {
        (void*)0,
        (__Block_byref_count_0 *)&count, 
        0, 
        sizeof(__Block_byref_count_0), 
        10
    };
    
    void (*blk)(void) = ((void (*)())&__main_block_impl_0(
    (void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_count_0 *)&count, 570425344));
    
    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);

    return 0;
}
__attribute__((__blocks__(byref))) __Block_byref_count_0 count = {
        (void*)0,
        (__Block_byref_count_0 *)&count, 
        0, 
        sizeof(__Block_byref_count_0), 
        10
    };

__Block_byref_count_0
struct __Block_byref_count_0 {
  void *__isa;
__Block_byref_count_0 *__forwarding;
 int __flags;
 int __size;
 int count;
};

相比起我们一开始编译出来没有使用__block说明符声明的代码中,多出了我们不太熟悉的一个指针

__forwarding    
//持有指向该实例自身的指针。而我们之所以能在block中修改外部变量的原因就在于此。
//原理就是,通过修改成员变量__forwarding访问成员变量count。(成员变量count是该实例自身持有的变量,它相当于block中的原自动变量)
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_count_0 *count = __cself->count; // bound by ref

        (count->__forwarding->count) = 11;
}
// 通过以上这段代码,大家就知道了,__block说明符就是通过修改__forwarding指针中的count变量从而达到修改外部变量的效果,多数时候我们可以推理出,能够修改一个地方达到修改其他地方的这种场景,无异于对同一块内存上的内容进行了修改,而能够达到这种效果的,便是指针的操作。

至于静态变量,静态全局变量和全局变量也能达到这个效果的办法,请大家自己编译源码查看下,其实原理也是通过修改指针来达到这个效果的。而这些变量的值是存在于__main_block_impl_0结构体中的

关于Blocks的存储

分析如何创建对应存储区的Block
上一篇 下一篇

猜你喜欢

热点阅读