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;
}
![](https://img.haomeiwen.com/i11779919/073338d8a02306ab.png)
block的变量捕获
auto局部变量,捕获到block内部,值传递
static局部变量,捕获到block内部,指针传递
全局变量, 不会捕获到block内部,直接访问
self是一个局部指针(self是每个对象函数的隐藏参数),故在block中使用self是会捕获的
block的类型
_NSGlobalBlock_ (没有访问auto变量, 分配在数据段)
_NSStackBlock_ (访问了auto变量, 分配在堆区)
_NSMallocBlock_ (_NSStackBlock调用了copy, 分配在栈区)
![](https://img.haomeiwen.com/i11779919/ff7ae33b66548434.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);
};
网友评论