美文网首页
Objective-C 基础之— Block本质+源码剖析

Objective-C 基础之— Block本质+源码剖析

作者: Harry_Coding | 来源:发表于2018-05-17 18:59 被阅读5次

block 又称之为“自带变量的匿名函数”,抛开OC语法定义block的形式不谈,其实好多语言都有类似的函数,比如JS的回调函数(其实就是将一个匿名还是作为函数的实参)、swift的闭包等等。。
首先讲一下oc block的实质,通过自身的理解,加以各位大神的剖析文章。block 在编译时期会被编译成结构体,也就是说OC的block底层是使用C语言结构体实现的, 和对象、类的实现是一样的(所以其实block就是OC中的一个对象),这个结构体包括两个结构体成员变量和一个构造函数,我们都知道Objective-C是用OC实现底层的,所以我们需要将代码转化为底层的实现(使用Clang -rewrite-objc [文件名], 这个命令是将OC语言转化成C或者C++的代码,经常使用这个命令我们窥探OC的底层奥秘)

.m 源码
#import <Foundation/Foundation.h>
int main(int argc, char * argv[]) {
    void (^myBlock)(void) = ^{
        NSLog(@"hello world");
    };
    myBlock();
return 0;
}

clang 转化成的过程源码

// 此结构体 存储了block的isa指针,还有函数指针(调用的时候正是找到此函数指针进行调用)
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
// block函数执行体转化 
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_dr_jkts5c395zs9xcx0bt_r7fx40000gn_T_main_7d68a8_mi_0);
    }
// 内存管理的结构体
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)};

// 转化之后的main函数,我们可以看到block被转化成了一个函数调用,这个函数正是 __main_block_impl_0 机构体的一个构造函数,所以block被转化成了一个 __main_block_impl_0 类型的结构体; 我们看到构造函数传入了两个参数,一个函数指针,一个是结构体__main_block_desc_0实例
int main(int argc, char * argv[]) {

// 其实这句话就是一个指针的赋值,将一个__main_block_impl_0结构体的指针赋值给我们定义的变量myBlock,从这里可以看,OC的block会转化成一个__main_block_impl_0类型结构体
    void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    // block 的调用, 这句代码的意思是找出__main_block_impl_0结构体的imp结构体(即是一个__block_impl的结构体)然后调用其函数指针FuncPtr,从而调用block内的函数执行体(当然这是过程代码,并没有直接标出myBlock.imp.FuncPtr)
    ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);

return 0;
}
// block的结构体 ,包含两个结构体和一个构造函数, 
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
  }
};

上面的源码转换成C代码之后,我做了一次完全的剖析,我们可以得出的结论就是,block 的实现其实就是一个结构体,而其的调用则是调用结构体内的函数指针调用函数;下面我将稍微改动一下代码,看一下生成的底层代码,研究一下,截获的外部变量的情况:

我只将不同的代码提出来
.m源代码
int main(int argc, char * argv[]) {
int a = 10;
void (^myBlock)(void) = ^{
    NSLog(@"hello world %d", a);
};
myBlock();
return 0;
}
clang 转换C代码
// 我们看出来了这个时候__main_block_func_0的结构体的构造函数,多传了一个变量a的参数,我们看到,并没有把a的变量指针传递,而是将变量的值传递
int main(int argc, char * argv[]) {
int a = 10;
void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);

return 0;
}

// block 函数执行体当然也会变了,生成了一个变量,这个变量是__main_block_impl_0结构体里的变量a
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int a = __cself->a; // bound by copy

    NSLog((NSString *)&__NSConstantStringImpl__var_folders_dr_jkts5c395zs9xcx0bt_r7fx40000gn_T_main_3e8c5a_mi_0, a);
}
// 重要的结构体来了,这个结构体内生成了一个成员变量a
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;
  }
};

那么我们可以看出,再没有任何修饰符的情况下,block截获的变量只是一个值,而并不是指针,所以,内部执行函数无法改变block外部的变量
通过上边的例子,我们可以猜想,如果block的结构体如果可以拥有变量的指针那么,就可以修改外部变量的值了 ,所以 _ block就是做这件事情的,其实_ block的实现远比我们想象的复杂,我们看一下__block 修饰之后转换的代码:

    // 我们可以看到加了__block 的变量和不加的变量发生明显的变化,加了__block的变量明显被转化成了一个结构体__Block_byref_c_0, 而且__main_block_impl_0block的结构体正是吧__Block_byref_c_0结构体类型的指针传入了
   int main(int argc, char * argv[]) {
    int a = 10;
    int b = 20;
    __attribute__((__blocks__(byref))) __Block_byref_c_0 c = {(void*)0,(__Block_byref_c_0 *)&c, 0, sizeof(__Block_byref_c_0), 30};
    void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a, b, (__Block_byref_c_0 *)&c, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);

return 0;
}
// 加了__block 的变量被转化的结构提类型
struct __Block_byref_c_0 {
  void *__isa;
  __Block_byref_c_0 *__forwarding;
 int __flags;
 int __size;
 int c;
};

// block的结构体也形成了一个__Block_byref_c_0结构体指针
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int a;
  int b;
  __Block_byref_c_0 *c; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int _b, __Block_byref_c_0 *_c, int flags=0) : a(_a), b(_b), c(_c->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
  }
};
// block的执行函数 也相应的生成了一个C结构体的指针指向block结构的C
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_c_0 *c = __cself->c; // bound by ref
  int a = __cself->a; // bound by copy
  int b = __cself->b; // bound by copy

    NSLog((NSString *)&__NSConstantStringImpl__var_folders_dr_jkts5c395zs9xcx0bt_r7fx40000gn_T_main_55b164_mi_0, a, b, (c->__forwarding->c));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->c, (void*)src->c, 8/*BLOCK_FIELD_IS_BYREF*/);}

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

所以我们可以看得出来_ block 的作用是生成外部变量的指针,从而达到在block内部可以修改的目的;下一篇文章将会结合这篇文章Block的本质,总结_ weak _ block _ strong的使用。
致谢:https://blog.csdn.net/abc649395594/article/details/47086751
https://www.jianshu.com/p/fdd7fa9a9e7e
两篇文章的对我的帮助!

相关文章

  • Objective-C 基础之— Block本质+源码剖析

    block 又称之为“自带变量的匿名函数”,抛开OC语法定义block的形式不谈,其实好多语言都有类似的函数,比如...

  • iOS源码解析:Block的本质<一>

    iOS源码解析:Block的本质<一> iOS源码解析:Block的本质<一>

  • block系列文章总结

    iOS源码解析:Block的本质<一>iOS源码解析:Block的本质<二>Objective C block背后...

  • block本质

    探究block本质fishhookHook Objective-C Block with Libffi如何使用li...

  • Python 源码剖析之基础知识

    Python 源码剖析之基础知识 为了和《Python 源码剖析》一书保持一致,本系列笔记分析的源码是 cpyth...

  • Objective-C之Blocks(三)

    前言 在Objective-C之Blocks(二)中,说明了Block的实质,本文在此基础上解释Block特性的实...

  • Objective-C Block Part2 - 实现原理

    Block 的本质 在 Objective-C Block Part1 中总结了 Block 的一些使用规则,但...

  • Block

    参考:Apple Block 源码Objective-C高级编程 iOS与OS X多线程和内存管理 Block与函...

  • 【iOS面试粮食】Block

    本文章将记录Objective-C中Block的相关资料,如有错误欢迎指出~ Block的本质 Block 实际上...

  • Block(一)

    一、Block本质 Block本质上是一个OC对象,内部也有个isa指针 二、Block的底层源码 1、测试代码 ...

网友评论

      本文标题:Objective-C 基础之— Block本质+源码剖析

      本文链接:https://www.haomeiwen.com/subject/bdjidftx.html