IOS底层

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

2019-03-06  本文已影响48人  Mr丨Yang

本章主要分析一下三个结论 (先给出结论,主要请自己阅读给出的源码,语言无法解释清楚)
一 :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 (下)

上一篇下一篇

猜你喜欢

热点阅读