一、block 是什么?
写了3年代码,说起 block听得最多的说法说的是 block 是一个匿名函数,那什么是 block?(自己见解,如果错误欢迎拍砖)
1、使用block 注意的地方?
其实我们写代码的时候,用到block 无非就是两点比较要注意。
1、会用__block 去修饰外部变量,原因吗多数也会说出来 block 不能捕获外部变量。
2、block 外部要用 weak 来修饰,内部用 strong 来修饰。这原因的也挺好说,无非是 block 在捕获外部变量的时候会保持一个强引用,当 block 内部捕获 self 的时候,由于对象对 block 进行 copy(也就是互相持有),就形成了循环引用。然后用了weakSelf后,在 block 里要是多次调用就可能被释放了,所以需要 block 中将 weakSelf “强化“。
然而少年,你以为这样就算理解了吗?太天真了。让我们继续往下走
block 的结构
* _ block impl: 这是一个结构体,也是C面向对象的体现,可以理解为block的基类;
struct __block_impl {
void *isa;int Flags;
int Reserved;
void *FuncPtr;
};
* _ main block_ impl_0: 可以理解为block变量;
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;
}
};
* _ main block func 0: 可以理解为匿名函数;
static void __main_block_func_0(struct
__main_block_impl_0 *__cself)
{
NSLog((NSString *)&__NSConstantStringImpl__var_folders_gc_5fkhcz0n6px48vzc744hmp6c0000gn_T_main_eef954_mi_0);
}
* _ main block desc 0:block的描述, Block_size;
{
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 (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)myblock)->FuncPtr)((__block_impl *)myblock);
}
return 0;
}
一串代码看着是不是很烦?我就谈谈我的理解吧
1、其实呢_main block impl 0 就是该 block 的实现,一个 block 实际是一个对象,它主要由一个 isa 和 一个 impl 和 一个 descriptor 组成。
在回头去看最开始的
1、 block 修饰的外部变量引用,是将栈上用block修饰的自动变量封装成一个结构体,让其在堆上创建。
__block int i = 1;
其实本质上会是 在__main_block_impl_0 转变成:
__Block_byref_i_0 *i;
i变量不再是int类型了,i变成了一个指向__Block_byref_i_0结构体的指针
在看__Block_byref _i _0
struct __Block_byref_i_0 {
void *__isa;
__Block_byref_i_0 *__forwarding;
int __flags;
int __size;
int count;
};
其实说白了,也就是复制了外部变量的内存地址。
2、block 循环引用的原因呢?说穿了也即是互相持有。
一个对象A有Block类型的属性,从而持有这个Block,如果Block的代码块中使用到这个对象A,或者仅仅是用用到A对象的属性,会使Block也持有A对象,导致两者互相持有,不能在作用域结束后正常释放。
解决原理:对象A照常持有Block,但Block不能强引用持有对象A以打破循环。
__weak typeof(self)
一些面试的时候可能会问的
1、为什么 block 使用 copy 修饰的?
因为 block 其实是在栈里的,ARC 自动 copy 到堆上,执行完了就会释放。所以外部调用需要用 copy,strong会造成野指针。