iOS 底层解析-----Block (上)

本章主要分析一下三个结论 (先给出结论,主要请自己阅读给出的源码,语言无法解释清楚)
一 :Block的本质
1.block 本质上是一个OC对象,它内部也有个isa指针
2.block 是封装了函数调用以及函数调用环境的OC对象

二:Block的变量捕获(capture)
变量捕获.png

三:Block的类型
block有三种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型
1. __NSGlobalBlock __ (_NSConcreteGlobalBlock)
2. __NSStackBlock __ (_NSConcreteStackBlock)
3. __NSMallocBlock __ (_NSConcreteMallocBlock)

一 :Block的本质

int main(int argc, const char * argv[]) {
    @autoreleasepool {
      //如下block代码 编辑之后
        void (^block)(void) = ^{
            NSLog(@"Hello, World!");
        };
       block();
    }
    return 0;
 }

//编译之后的block代码 抽取核心代码
// 1. main中的block编译之后
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
        // 定义block变量
        void (*block)(void) = &__main_block_impl_0(
                                                   __main_block_func_0,
                                                   &__main_block_desc_0_DATA
                                                   );

        // 执行block内部的代码
        block->FuncPtr(block);
    }
    return 0;
}

// 2.  __main_block_impl_0函数第一个参数:__main_block_func_0, 封装了block执行逻辑的函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_c60393_mi_0);
        }

//3. __main_block_impl_0函数第二个参数:_main_block_desc_0 代表了Block在内存中占用的大小
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)};

//4. __main_block_func_0中  __main_block_impl_0 结构体如下
struct __main_block_impl_0 {
  struct __block_impl impl;//这个地方存放的是指针对应的值 也就是存在着struct __block_impl结构体 里面包含有isa指针
  struct __main_block_desc_0* Desc;
  // 构造函数(类似于OC的init方法),返回结构体对象
  __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;
  }
};

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

-------------------------------------分割线-----------------------------------

//block 带参数
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        void (^block)(int, int) = ^(int a, int b){
           NSLog(@"Hello, World! - %d %d", a, b);
        };
     block(10, 20);
    }
    return 0;
 }
//编译之后

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        void (*block)(int, int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
        block->FuncPtr(block, 10, 20);
    }
    return 0;
}
//调用的方法函数 将参数作为自己的参数进行传递
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_87bc8b_mi_0, a, b);
        }

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)};

二:Block的变量捕获(capture) 从代码分析

//局部变量解析
void (^block)(void);

void test() 
{
    //在test方法中,int 变量是局部变量 默认属性为auto 可以不写
    // auto:自动变量,离开作用域就销毁
   // auto int age = 10;
    int age = 10;
  //增加static 作为局部变量,在test 方法调用结束后所占内存并不会销毁
    static int height = 10;

    block = ^{
        NSLog(@"age is %d, height is %d", age, height);
    };
    
    age = 20;
    height = 20;
}
//运行之后看结果 age is 10, height is 20
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test();
        block(); 
    }
    return 0;
}
 看编译之后的代码

void (*block)(void);
-------重点-------
//block将 age height 作为结构体的一部分
struct __test_block_impl_0 {
  struct __block_impl impl;
  struct __test_block_desc_0* Desc;
  int age; //这个地方的age 因为是auto 所以第一次传进来的时候,age初始的值10被捕获到,所以age 最终为10 
  int *height;//这个height 为static  它也会捕获但是它捕获的是height的内存中的指针,并不在意你初始化值,在运行block()之后,它会去取指针对应的值,也就是20
  __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int _age, int *_height, int flags=0) : age(_age), height(_height) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
  int age = __cself->age; // bound by copy  
  int *height = __cself->height; // bound by copy


        NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_d2875b_mi_0, age, (*height));
    }

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)};
void test()
{
    int age = 10;
    static int height = 10;

    block = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA, age, &height));

    age = 20;
    height = 20;
}

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        test();
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
-------------------------------分割线-------------------
//全局变量解析
int age_ = 10;
static int height_ = 10;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        void (^block)(void) = ^{
          NSLog(@"age is %d, height is %d", age, height);
        };
    age_ = 20;
    height_ = 20;
     block();
    }
    return 0;
 }
//编译之后
int age_ = 10;
static int height_ = 10;

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;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_c60393_mi_0,age_,height_ );
        }

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)};



struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
        void (*block)(void) = &__main_block_impl_0(
                                                   __main_block_func_0,
                                                   &__main_block_desc_0_DATA
                                                   );
         age_ = 20;
         height_ = 20;
        block->FuncPtr(block);
    }
    return 0;
}
总结: 因为它是全局的,可以直接取值,所以block 不需要捕获

三:Block的类型

注意:该段代码验证要将ARC编译环境改为MRC
int main(int argc, const char * argv[]) {
    @autoreleasepool {
         int age = 10;
         //Global:没有访问auto变量
        void (^block1)(void) = ^{
            NSLog(@"Hello");
        };
        //Stack:访问了auto变量
        void (^block2)(void) = ^{
            NSLog(@"Hello - %d", age);
        };
       
        NSLog(@"%@ %@ %@", [block1 class], [block2 class]);
        打印结果:_NSGlobalBlock_     _NSStackBlock_
    }
    return 0;
}

---------------代码片段二-------------------------
void (^block)(void);
void test()
{
    int age = 10;
  //Stack:访问了auto变量
    block =^{
        NSLog(@"block------%d",age);
    }
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
      test();
      //问题:Stack存在于栈中,栈中的数据可能会销毁,所以age取值可能已经销毁掉,取值会存在错误(MRC环境)
      block();
        
    }
    return 0;
}
以上代码问题如果给block 增加copy字段会将值从栈中捕获到堆中,取值会变成正常
void test()
{
    int age = 10;
    //增加copy 变成Malloc
    block =[^{
        NSLog(@"block------%d",age);
    } copy];
}

以上代码表示的三种类型 在内存中的分配如下图

三种类型.png

.text区 放代码段
.data区 一般存放全局变量
存放在堆内存中,相当于alloc出来的,需要程序员申请,也需要程序员自己管理内存
存放在栈中,系统自动分配,调用完成之后就销毁

总结如下


Block类型.png

iOS 底层解析-----Block (下)

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

推荐阅读更多精彩内容