block

作者: guoguojianshu | 来源:发表于2021-08-12 11:38 被阅读0次

没有参数没有返回值的block

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        void (^myBlock)(void) = ^{
            
            NSLog(@"Hello, World!");
        };
        myBlock();
    }
    return 0;
}

clang后的代码

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
  • 代码为
struct __main_block_impl_0 {
//    因为impl是结构体的一个变量,所以这个impl的地址和结构体的地址一样
  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;
  }
};
//封装了block执行逻辑的函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {


            NSLog((NSString *)&__NSConstantStringImpl__var_folders_4h_vk7vdxt51k1c4mbrt28057j80000gn_T_main_73f599_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)};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
//定义block的变量
//        给block的 结构体的impl的 funcptr和Desc进行 赋值
        void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
//        执行block内部的代码
//        调用block中的funcptr函数,并把block对象一起当参数传入
        ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
    }
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

block值的捕获

        int age = 10;
        void (^myBlock)(void) = ^{
//       值的捕获
            NSLog(@"age is %d",age);
        };
        age = 20;
        myBlock();
  • clang后的代码
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
//    block内部多个age变量,和使用外面的变量名一样
  int age;
//age(_age),这个语法的意思是把参数_age赋值给变量age
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
//    从block对象中取出age值
  int age = __cself->age; // bound by copy


            NSLog((NSString *)&__NSConstantStringImpl__var_folders_4h_vk7vdxt51k1c4mbrt28057j80000gn_T_main_c5a332_mi_0,age);
        }

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(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        int age = 10;
//        这里把age的值传给block的结构体,值传递
        void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
        age = 20;
        ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
    }
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

block捕获局部变量,自动变量是值传递,静态变量是指针传递

//        auto:自动变量,离开作用域就销毁,因为创建的变量都有个auto(默认就是auto),这个是c语言的,局部变量分为三种,一种为auto,static,register
        
//      auto  int age = 10;
        int age = 10;
        static int height = 10;
        register int regic = 40;
        
        void (^myBlock)(void) = ^{
//       值的捕获
            NSLog(@"age is %d, height is %d, regic is %d",age,height,regic);
        };
        age = 20;
        height = 20;
        regic = 50;
        myBlock();
  • clang后的代码
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int age;
//保存的是指针(内存地址)
  int *height;
  int regic;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int *_height, int _regic, int flags=0) : age(_age), height(_height), regic(_regic) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int age = __cself->age; // bound by copy
  int *height = __cself->height; // bound by copy
  int regic = __cself->regic; // bound by copy

// *height表示的是去height这个内存地址里面所存储的数据
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_4h_vk7vdxt51k1c4mbrt28057j80000gn_T_main_d3b457_mi_0,age,(*height),regic);
        }

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(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
//        自动变量(auto),可能随时会被销毁
        int age = 10;
//      静态变量,一直在内存中存在
        static int height = 10;
//       考虑使用寄存器存储
        register int regic = 40;
//静态变量修饰的height被传入的参数是指针(一个内存地址)
        void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age, &height, regic));
        age = 20;
        height = 20;
        regic = 50;
        ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
    }
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

block不会捕获全局变量,直接访问

#import <Foundation/Foundation.h>
int age = 10;
static int height = 10;
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
                void (^myBlock)(void) = ^{
        //       全局变量
                    NSLog(@"age is %d, height is %d",age,height);
                };
                age = 20;
                height = 20;
                myBlock();
    }
    return 0;
}
  • clang后的代码
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_4h_vk7vdxt51k1c4mbrt28057j80000gn_T_main_8555c3_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)};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
                void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
                age = 20;
                height = 20;
                ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
    }
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

block对self捕获,因为self是局部变量,每个方法都会默认有self和_cmd这两个局部参数

@implementation JGPerson
-(instancetype)initWithName:(NSString *)name{
    if (self = [super init]) {
        self.name = name;
    }
    return self;
}
-(void)test{
    void (^myBlock)(void) = ^{
        NSLog(@"%p",self);
    };
    myBlock();
}
@end
  • clang编译后
static instancetype _Nonnull _I_JGPerson_initWithName_(JGPerson * self, SEL _cmd, NSString * _Nonnull name) {
    if (self = ((JGPerson *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("JGPerson"))}, sel_registerName("init"))) {
        ((void (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)self, sel_registerName("setName:"), (NSString * _Nonnull)name);
    }
    return self;
}

struct __JGPerson__test_block_impl_0 {
  struct __block_impl impl;
  struct __JGPerson__test_block_desc_0* Desc;
//    捕获了 self
  JGPerson *self;
  __JGPerson__test_block_impl_0(void *fp, struct __JGPerson__test_block_desc_0 *desc, JGPerson *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __JGPerson__test_block_func_0(struct __JGPerson__test_block_impl_0 *__cself) {
  JGPerson *self = __cself->self; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_4h_vk7vdxt51k1c4mbrt28057j80000gn_T_JGPerson_17b7a8_mi_0,self);
    }
static void __JGPerson__test_block_copy_0(struct __JGPerson__test_block_impl_0*dst, struct __JGPerson__test_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __JGPerson__test_block_dispose_0(struct __JGPerson__test_block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __JGPerson__test_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __JGPerson__test_block_impl_0*, struct __JGPerson__test_block_impl_0*);
  void (*dispose)(struct __JGPerson__test_block_impl_0*);
} __JGPerson__test_block_desc_0_DATA = { 0, sizeof(struct __JGPerson__test_block_impl_0), __JGPerson__test_block_copy_0, __JGPerson__test_block_dispose_0};
//test方法,可以 看到self和_cmd都是局部参数
static void _I_JGPerson_test(JGPerson * self, SEL _cmd) {
    void (*myBlock)(void) = ((void (*)())&__JGPerson__test_block_impl_0((void *)__JGPerson__test_block_func_0, &__JGPerson__test_block_desc_0_DATA, self, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}

static NSString * _Nonnull _I_JGPerson_name(JGPerson * self, SEL _cmd) { return (*(NSString * _Nonnull *)((char *)self + OBJC_IVAR_$_JGPerson$_name)); }
static void _I_JGPerson_setName_(JGPerson * self, SEL _cmd, NSString * _Nonnull name) { (*(NSString * _Nonnull *)((char *)self + OBJC_IVAR_$_JGPerson$_name)) = name; }

总结

  • block本质是一个oc对象,它的内部有个isa指针
  • block是 封装了函数调用以及函数调用环境的oc对象
  • block的底层结构如下: block的结构

block捕获的情况

block捕获情况

block的类型

block的类型
注意如果是在mrc情况下,访问了auto变量的block是在栈空间的,也就是stackblock,这样会被栈自动释放,如果要使用,就得使用copy,从栈拷贝到堆空间中,不能让他自动释放掉,不在使用时候得releas掉,再arc使用时候,访问auto变量,就是mallocblock了
在使用clang转换OC为C++代码时,可能会遇到以下问题
cannot create __weak reference in file using manual reference

解决方案:支持ARC、指定运行时系_统版本,比如
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m

对象类型的auto变量

  1. 当block内部访问了对象类型的auto变量时
  • 如果block在栈上,将不会对auto变量产生强引用
  • 如果block被copy到堆上
  • 会调用 block内部的copy函数,copy函数内部会调用_Block_object_assign函数,_Block_object_assign函数会根据auto变量的修饰符(__weak,__strong,__unsafe_unretained)做出相应的操作,形成强引用或弱引用
  1. 如果block从堆上移除
  • 会调用block内部的dispose函数
  • dispose函数内部会调用_Block_object_dispose函数,这个函数会自动释放引用的auto变量,类似release操作,引用计数器-1


    copy函数和dispose函数调用时机

使用__block来修饰变量

#import <Foundation/Foundation.h>

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

struct __Block_byref_a_0 {
  void *__isa;
  struct __Block_byref_a_0 *__forwarding;
 int __flags;
 int __size;
 int a;
};
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  struct __Block_byref_a_0 *a; // by ref

};

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
       __block int a = 10;
        void (^myBlock)(void) = ^ {
            a = 20;
        };
//        myBlock();
        struct __main_block_impl_0 *blockImpl = (__bridge struct __main_block_impl_0 *)myBlock;
        NSLog(@"%@",&a);
    }
    return 0;
}

使用__block会把变量,包装成一个对象(一个结构体里面有isa指针),block持有这个结构体,对这个结构体进行内存管理

相关文章

  • iOS开发之Block原理探究

    Block概述 Block本质 Block调用 Block分类 Block循环引用 Block原理探究 Block...

  • block的使用

    定义block 返回类型 (^block名称)(参数) = ^(){block内容}; 调用block block...

  • Block 02 - __block

    Block 02 - __block __block 的作用 __block 可以解决 Block 内部无法修改 ...

  • iOS面试之Block大全

    Block Block内容如下: 关于Block 截获变量 __block修饰符 Block的内存管理 Block...

  • iOS面试之Block模块

    Block Block内容如下: 关于Block 截获变量 __block修饰符 Block的内存管理 Block...

  • iOS Block

    Block的分类 Block有三种类型:全局Block,堆区Block,栈区Block 全局Block 当Bloc...

  • iOS block 为什么官方文档建议用 copy 修饰

    一、block 的三种类型block 三种类型:全局 block,堆 block、栈 block。全局 block...

  • iOS开发block是用copy修饰还是strong

    Block分为全局Block、堆Block和栈Block1、在定义block没有引用外部变量的时候,block为全...

  • block 初探

    全局block, 栈block, 堆block

  • Block

    一、Block本质 二、 BlocK截获变量 三、__block 修饰变量 四、Block内存管理 五、Block...

网友评论

      本文标题:block

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