一步步学习ios

block底层原理学习

2020-05-26  本文已影响0人  冷武橘

一、block的本质


  int a = 10;
    void (^Block)(void) = ^{
        NSLog(@"%d",a);
    };

使用clang转换OC为C++代码


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;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int a = __cself->a; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_b4_xvb__z9916b4jgm4xkg60n_00000gn_T_main_0adf31_mi_0,a);
    }

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[]) {
    int a = 10;
    void (*Block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
    return 0;
}
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

通过c++代码可见block内部有个isa指针,本质上也是一个oc对象。

二、block的变量捕获

2.1、值传递

int main(int argc, const char * argv[]) {
    int a = 10;
    void (^Block)(void) = ^{
        NSLog(@"%d",a);
    };
    return 0;
}
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;
  }
};
int main(int argc, const char * argv[]) {
    int a = 10;
    void (*Block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
    return 0;
}

block在创建时,传入了变量a的值,并将a的值赋给了main_block_impl_0的成员a,整个过程是值传递。

2.1、指针传递

int main(int argc, const char * argv[]) {
    static  int a = 10;
    void (^Block)(void) = ^{
        NSLog(@"%d",a);
    };
    return 0;
}
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;
  }
};

int main(int argc, const char * argv[]) {
    static int a = 10;
    void (*Block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &a));
    return 0;
}

block在创建时,传入了变量a的地址,并将a的地址赋给了main_block_impl_0的成员*a,整个过程是指针传递。

2.3、全局变量

static  int a = 10;
int b = 21;
int main(int argc, const char * argv[]) {

    void (^Block)(void) = ^{
        NSLog(@"%d",a);
        NSLog(@"%d",b);
    };
    return 0;
}
static int a = 10;
int b = 21;

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_b4_xvb__z9916b4jgm4xkg60n_00000gn_T_main_263450_mi_0,a);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_b4_xvb__z9916b4jgm4xkg60n_00000gn_T_main_263450_mi_1,b);
    }

int main(int argc, const char * argv[]) {

    void (*Block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    return 0;
}

当a,b作为全局变量时,block就不会捕获到内部,直接访问就可以了。


截屏2020-05-26 上午11.36.58.png

2.3、测试题

static  int a = 10;
int b = 21;
int main(int argc, const char * argv[]) {
    static c = 22;
    int d = 20;
    void (^Block)(void) = ^{
        NSLog(@"%d",a);
        NSLog(@"%d",b);
        NSLog(@"%d",c);
        NSLog(@"%d",d);
  };
    a = 5;
    b = 5;
    c = 5;
    d = 5;
    Block();
    return 0;
}

只有c的值还是22,不会被修改,因为block内部捕获到的变量是值传递。

三、block的类型

block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型:
NSGlobalBlock ( _NSConcreteGlobalBlock )
NSStackBlock ( _NSConcreteStackBlock )
NSMallocBlock ( _NSConcreteMallocBlock )

截屏2020-05-26 下午2.00.47.png

3.1、MRC

nt main(int argc, const char * argv[]) {
    int a = 10;
    void (^block1)(void) = ^{
        
       NSLog(@"%d",a);
    };

    static  int b = 10;
   void (^block2)(void) = ^{
          
       NSLog(@"%d",b);
    };

   void (^block3)(void) = ^{
           
       
     };
    void (^block4)(void) = ^{
              
         NSLog(@"%d",c);
    };
    void (^block5)(void) = ^{
              
         NSLog(@"%d",d);
    };
    NSLog(@"%@",[block1 class]); __NSStackBlock__
    NSLog(@"%@",[block2 class]);__NSGlobalBlock__
    NSLog(@"%@",[block3 class]);__NSGlobalBlock__
    NSLog(@"%@",[block4 class]);__NSGlobalBlock__
    NSLog(@"%@",[block5 class]);__NSGlobalBlock__
    
    return 0;
}
    int a = 10;
    void (^block)(void) = ^{
        
       NSLog(@"%d",a);
    };
    NSLog(@"%@",[block class]); __NSStackBlock__
    NSLog(@"%@",[[block copy] class]); __NSStackBlock__
   

每一种类型的block调用copy后的结果如下所示:


image.png image.png

3.2、ARC

  static  int b = 10;
     void (^block2)(void) = ^{
            
         NSLog(@"%d",b);
      };

     void (^block3)(void) = ^{
             
         
       };
      void (^block4)(void) = ^{
                
           NSLog(@"%d",c);
      };
      void (^block5)(void) = ^{
                
           NSLog(@"%d",d);
      };
      NSLog(@"%@",[block2 class]); __NSGlobalBlock__
      NSLog(@"%@",[block3 class]);__NSGlobalBlock__
      NSLog(@"%@",[block4 class]);__NSGlobalBlock__
      NSLog(@"%@",[block5 class]);__NSGlobalBlock__
    int a = 10;
1、强指针
      void (^block1)(void) = ^{
          
         NSLog(@"%d",a);
      };
 2、弱指针
     __weak void (^block2)(void) = ^{
             
            NSLog(@"%d",a);
      };
     NSLog(@"%@",[block1 class]);__NSMallocBlock__
     NSLog(@"%@",[block2 class]); __NSStackBlock__

 3、没有指针
     NSLog(@"%@", [^{
                 
                NSLog(@"%d",a);
     }class]);__NSStackBlock__
    
@property (copy, nonatomic) void (^testBlock)(void);
@property (weak, nonatomic) void (^test1Block)(void);
@property (strong, nonatomic) void (^test2Block)(void);

    int   a = 10;
    self.testBlock = ^(){
        NSLog(@"%d",a);

        
    };
    self.test1Block = ^(){
             NSLog(@"%d",a);
      };
    self.test2Block = ^(){
             NSLog(@"%d",a);
      };
    NSLog(@"%@",[self.testBlock class]);__NSMallocBlock__
    NSLog(@"%@",[self.test1Block class]);__NSStackBlock__
    NSLog(@"%@",[self.test2Block class]);__NSMallocBlock__
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    self.test1Block();

}

self.test1Block的类型是_NSStackBlock,存储在栈区。过了作用域就会销毁,这样再次调用就会访问了坏内存,造成崩溃。这个例子也说明了MRC下block属性为什么一般用copy,就是 为了内存的访问安全考虑。

3.3、小结

3.4、对象类型的auto变量

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

4、__block修饰符

__block可以用于解决block内部无法修改auto变量值的问题
_block不能修饰全局变量、静态变量(static)
编译器会将__block变量包装成一个对象

 __block int a = 10;
      void (^block1)(void) = ^{
          
          a = 0;
      };
struct __Block_byref_a_0 {
 void *__isa;
__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;
 __Block_byref_a_0 *a; // by ref
 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
   impl.isa = &_NSConcreteStackBlock;
   impl.Flags = flags;
   impl.FuncPtr = fp;
   Desc = desc;
 }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
 __Block_byref_a_0 *a = __cself->a; // bound by ref


         (a->__forwarding->a) = 0;
     }

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), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
  __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};
     void (*block1)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
   return 0;
}
本质就是 __block变量被编译器 包装成 __Block_byref_a_0 对象,__main_block_func_0接收的是一个对象,再通过修改对象的指针修改age的值。
上一篇下一篇

猜你喜欢

热点阅读