block源码分析一

#include "stdio.h"  
int main()  
{  
    void (^block)(void) = ^(void)  
    {printf("aaa\n");};  
    block();  
    return 0;  
}  

//那两行代码竟然增加到了那么多,可以从里面看出来,最终block匿名函数会用C语言的函数指针来处理

struct __block_impl {  
    void *isa;  
    int Flags;  
    int Reserved;  
    voidvoid *FuncPtr;  
};  
  
struct __main_block_impl_0 {  
  struct __block_impl impl;  
  struct __main_block_desc_0* Desc;  
  __main_block_impl_0(voidvoid *fp, struct __main_block_desc_0 *desc, int flags=0) {  
    impl.isa = &_NSConcreteStackBlock;  
    impl.Flags = flags;  
    impl.FuncPtr = fp;  
    Desc = desc;  
  }  
};  
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {  
printf("aaa\n");}  
  
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()  
{  
 void (*block)(void) = ((void (*)())&__main_block_impl_0((voidvoid *)__main_block_func_0, &__main_block_desc_0_DATA));  
 ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);  
 return 0;  
}  

源码结构分析部分

1.block实际的结构体部分(本体)

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

首先impl和Desc也是两个结构体,而__main_block_imp_0就是该结构体的构造函数,用来初始化,后面细细介绍

2.我们先来看看第一个成员变量impl

struct __block_impl {  
    voidvoid *isa;  
    int Flags;  
    int Reserved;  
    voidvoid *FuncPtr;  
};  

在objc中,id代表了一个对象。根据上面的声明,凡是首地址是isa的struct指针,都可以被认为是objc中的对象
isa指针,这个很熟悉吧,这就能看出其实block其实就是对象

他的第一个属性也是一个结构__block_impl,而第一个参数也是一个isa的指针。在运行时,NSObject和block的isa指针都是指向对象的一个8字节。NSObject及派生类对象的isa指向Class的prototype,而block的isa指向了_NSConcreteStackBlock这个指针。就是说当一个block被声明的时候他都是一个_NSConcreteStackBlock类的对象。

flags和reserved这两个基本就是某些标记(暂时没那么重要)FuncPtr这个就是函数指针,也就是block所需要执行的代码段,真正存的地址

3.第二个成员变量Desc

static struct __main_block_desc_0 {  
  size_t reserved;  
  size_t Block_size;  
}  

顾名思义reserve和Block_size分别代表了版本升级所需的区域和Block的大小

4.第三个就是这个结构体的构造函数(可以理解为对象的初始化方法)

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

可以通过这里看出,初始化的时候会右fp指针(这里就是函数指针),以及desc结构体的指针传进来初始化,void fp的指针赋值给了FuncPtr指针,以上的结构和初始化函数就是基本的Block生成的源代码

源码调用部分分析

5.现在来看看Main函数中调用的基本转换(初始化转换)

void (*block)(void) = ((void (*)())&__main_block_impl_0((voidvoid *)__main_block_func_0, &__main_block_desc_0_DATA));  
// 调用结构体函数初始化  
struct __main_block_impl_0 impBlock = __main_block_impl_0(__main_block_func_0,&__main_block_desc_0_DATA);  
  
// 赋值给该结构体类型指针变量  
struct __main_block_impl_0 *block = &impBlock;  

就是把栈上生成的__main_block_impl_0的结构体实例的指针,赋值给__main_block_impl_0结构体指针类型的变量block

__main_block_func_0就是转换成的函数指针,这样void (*block)(void) = ^{}这句简单的代码最终就是上面的结构体初始化函数的内部实现逻辑

6.再来细看下初始化函数内部的实现__block_main_impl_0

__main_block_impl_0(__main_block_func_0,&__main_block_desc_0_DATA);  

第一个参数是由Block块语法转换的正真内部函数指针,第二个参数就是作为静态全局变量初始化__main_block_desc_0的结构体实例指针,初始化如下

__main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};  

最终,初始化内部进行赋值

isa = &_NSConcreteStackBlock

Flags = 0

Reversed = 0

FuncPtr = __main_blcok_func_0(就是Block块代码转换成的C语言函数指针)

Desc = &__ main_block_desc_0_DATA

7.最终block()调用的内部实现

((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);  

去掉类型就是:

(*block->imp.FuncPtr)(block);
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {  
printf("aaa\n");} 

最后就是直接调用函数指针进行最终的调用,由上面所描述的,FuncPtr就是由__main_block_func_0的函数指针所赋值的指针,而且可以看出,这个函数的参数正是指向block本身结构体实例,block作为参数进行了传递

objc_object结构体有个Class isa,这个isa指向下面这个结构体

struct objc_class {

    Class isa  OBJC_ISA_AVAILABILITY;



#if !__OBJC2__

    Class super_class                                        OBJC2_UNAVAILABLE;

    constchar *name                                         OBJC2_UNAVAILABLE;

    long version                                             OBJC2_UNAVAILABLE;

    long info                                                OBJC2_UNAVAILABLE;

    long instance_size                                       OBJC2_UNAVAILABLE;

    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;

    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;

    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;

    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;

#endif



}

isa指针__NSConcreteStackBlock相当于class的结构体实例,关于该类的所有信息都包含在这个地址里面,跑起来的时候就可以通过该指针去父类找需要的东西了,那么Block最简单的实质应该有个初步了解了吧,明白了Block其实就是OC里面的对象了

概括下:

  1. __main_block_impl_0这个就是Block内部的结构体(该结构体成员有impl,Desc 和一个该结构体的初始化函数)

2.Block块内的代码转换成了__main_block_func_0的c语言函数(参数就是__main_block_impl_0结构体)

3.Block语法的初始化实际就是将__main_blcok_impl_0的结构体实例化,重点是Block的代码块{}通过转换成C的函数指针进行结构体的成员变量FuncPtr指针赋值

4.Block调用就是通过实例化的结构体里面的FuncPtr指针就行函数调用,而且参数就是该结构体本身(暂时没用到这个传出去的结构体,另外写一个介绍带参数是如何完成回调的,如何截获变量等等)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容