zoukankan      html  css  js  c++  java
  • Objective-C 基础之— Block本质+源码剖析

     

    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 
    两篇文章的对我的帮助!

     
     
     

  • 相关阅读:
    “贴身外教”是英语口语的学习方法综合解决方案
    浅谈提高英语口语的最有效方法
    【PERL】Perl默认的内部变量
    Perl——哈希的创建和引用
    linux和windows下,C/C++开发的延时函数,sleep函数
    linux标准库#include <unistd.h>与windows的#include <windows.h>(C语言开发)
    linux 如何显示一个文件的某几行(中间几行)
    使程序在Linux下后台运行
    Perl中关于数组的输出——需要注意的地方
    linux下C/C++,多线程pthread
  • 原文地址:https://www.cnblogs.com/wannaGoBoy/p/9052697.html
Copyright © 2011-2022 走看看