block的原理是什么

block的原理是什么

  • block本质上是一个OC对象,它内部也有isa指针
  • block是封装了函数调用以及函数调用环境的OC对象
看一下这个block被编译器转换后的底层结构:
int age = 5;
void (^block)(int, int) = ^(int a, int b) {
    NSLog(@"this is a block %d %d", a, b);
    NSLog(@"this is a block - %d",age);
};
block(10, 20);

block的底层结构:
struct __block_impl {
  void *isa;  //!!!!
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int 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, int a, int b) {
  int age = __cself->age; // bound by copy

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_7h_7l3g9v_x21q8mng59_xy7cbm0000gn_T_main_579d88_mi_0, a, b);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_7h_7l3g9v_x21q8mng59_xy7cbm0000gn_T_main_579d88_mi_1,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 = 5;
        void (*block)(int, int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
        ((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 10, 20);
    }
    return 0;
}
block结构.png

block的变量捕获

auto局部变量,捕获到block内部,值传递
static局部变量,捕获到block内部,指针传递
全局变量, 不会捕获到block内部,直接访问

self是一个局部指针(self是每个对象函数的隐藏参数),故在block中使用self是会捕获的

block的类型

_NSGlobalBlock_ (没有访问auto变量, 分配在数据段)
_NSStackBlock_ (访问了auto变量, 分配在堆区)
_NSMallocBlock_ (_NSStackBlock调用了copy, 分配在栈区)

内存分配.png

block使用auto变量

当block在栈上(_NSStackBlock_), 是不会对auto变量产生强引用

在block被拷贝堆上
会调用block内部的copy函数,
copy函数内部会调用_Block_object_assign函数:
_Block_object_assign会根据auto变量的修饰符(__strong,__weak,__unsafe_unretained)作出相对应操作,形成强引用(retain )或弱引用 (注意这里仅限ARC下,MRC下不会进行retain操作)

如果block从堆上移除
会调用block内部的dispose函数,
dispose函数会调用_Block_object_dispose函数:释放引用的auto变量,类似release

__block修饰符的作用

__block可以用于解决block内部无法修改auto变量值的问题
__block只能修饰auto变量
编译器会将__block修饰的变量,包装为一个对象;

struct __Block_byref_age_0 {
  void *_isa;
  __Block_byref_age_0 *__forwarding; 
  int __flags;
  int __size;
  int age;
}
//赋值操作
__Block_byref_age_0 *age = __cself->age;
(age->__forwarding->age) = 20;

-> 为什么__block修饰的变量可以被block中进行修改,本质上的原因是通过指针进行修改

当block在栈上, __forwarding指向自己
当block在堆上, __forwarding指向复制到堆上的__block结构体,而堆上的__forwarding指向自己
__weak typeof(self) weakSelf = self;
self.block = ^{
  __strong typeof(weakSelf) = sSelf; 
  -> 1-通过此方式可以直接调用成员变量 2-保证在执行block中该self的生命
 NSLog("%d",self->_age);
};
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容