Block
block基本概念
block的本质是一个OC对象
因为block底层结构的第一个成员实际上是一个isa指针。
block封装了函数调用和函数调用环境。
因为block的内部存储了block函数实现的入口地址(函数调用),还捕获了
block函数所用到的外部的一些变量(函数调用环境)。
@property (nonatomic, copy) void(^block)(void);
值捕获
1.局部变量会被block捕获
自动变量(auto),block通过值拷贝方式捕获,在其内部创建一个同类型变
量,并且将自动变量的值拷贝给block的内部变量,block代码块执行的时候,直
接访问它的这个内部变量。
静态变量(static),block通过地址拷贝方式捕获,在其内部创建一个指向同类
型变量的指针, 将静态变量的地址值拷贝给block内部的这个指针,block代码块
执行的时候,通过内部存储的指针间接访问静态变量。
2.全局变量不会被block捕获
block代码块执行的时候,通过全局变量名直接访问。
self值捕获
block 内部使用 self.age
self会被捕获
block 内部使用 _age
self会被捕获,_age 的写法等同与self->_age
内存位置
全局区
NSGlobalBlock 数据段
如果一个block内部没有使用/访问 自动变量(auto变量),那么它的类型即为
__NSGlobalBlock__,它会被存储在应用程序的 数据段
栈block
NSStaticBlock 栈区
MRC :如果一个block有使用/访问 自动变量(auto变量),那么它的类型即为
__NSStaticBlock__,它会被存储在应用程序的 栈区
堆block
NSMallocBlock 堆区
ARC :如果一个block有使用/访问 自动变量(auto变量),自动从栈区copy到堆
区,就可以转变成__NSMallocBlock__,它会被存储在堆区上
ARC下auto变量可能被释放了,影响block获取变量,所以干脆复制到堆上
对__NSMallocBlock__调用copy方法,就可以转变成__NSMallocBlock__,它会
被存储在堆区上
ARC栈copy到堆 1
在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上
ARC栈copy到堆2
1.block作为函数参数返回的时候
2.将block赋值给__strong指针的时候
3.block作为Cocoa API中方法名里面含有usingBlock的方法参数时
4.block作为GCD API的方法参数的时候
__xxx_block_copy_x(
struct __main_block_impl_0*dst, // 拷贝之前栈空间的block
struct __main_block_impl_0*src // 拷贝之后堆空间上的
block
);
(1)CLPerson *person 对象局部变量在被block捕获后,出了作用域并没有自
动释放
【ARC环境-->堆上的block-->强指针CLPerson *person 】
没有显试weak修饰的相当于strong 强指针
会引用该对象,retain 后引用奇计数+1,当block释放时,通过
_Block_object_dispose 来计数-1
(2)使用弱指针__weak CLPerson *person 局部变量在被block捕获后,出了
作用域并 自动释放了
(3)__weak + __strong : 只有外部使用weak时,避免对象过早释放,而
block内 刚好有 【延迟方法】时,会无法获取对象,起到一个延迟释放作用
ARC:__block
__block结构
struct __Block_byref_val_0 {
void *__isa; // isa指针
__Block_byref_val_0 *__forwarding;
int __flags;
int __size; // Block结构体大小
int age; // 捕获到的变量
}
__block只可以用来作用于auto变量,它的目的就是为了能够让auto变量能够在
block内部内修改,这才有了 __Block_byref_val_0 结构体
被捕获的对象不再直接操作,而是将其封装到 __Block_byref_val_0 结构体里
面,通过这个结构体来操作 变量。复制到堆上就是将结构体直接复制,然后修改
指针
__forwarding
栈block
__forwarding 指针就是指向栈上的自己
栈 -> 堆block
栈上的__forwarding 指针就是指向堆上的__Block_byref_val_0 结构体
堆上的__forwarding 指针就是指向 堆上的自己
_block + 基本类型变量 int a
将int a包装在struct __Block_byref_a_0内部,这样block实际上捕获的是这个
struct __Block_byref_a_0,它可以被当作一个对象来看待,因此需要通过
retain 和 release 管理内存
__block + 对象变量
相比较基本类型变量,对象类型的变量被__block修饰后,底层所生成的
__Block_byref_xxx_x结构体里面多了两个函数指针,
__Block_byref_id_object_copy和__Block_byref_id_object_dispos
__block + __weak + 对象变量
加上了__weak,因此struct __Block_byref_xxx_x内部封装的就是一个指向对
象的弱指针CLPerson *__weak weakBlockPerson
__strong
为避免循环引用使用 __weak, 但是常规都可能是多线程环境,对象可能被提前
释放,此时block内部有延时函数就会导致对象获取nil。
而__strong在Block内部修饰的对象,会保证在使用这个对象在scope内,这个对象
都不会被释放,出了scope,引用计数就会-1,
__weak typeof(self) weakSelf = self;
model.dataChanged = ^(NSString *title) {
__strong typeof(self) strongSelf = weakSelf;
strongSelf.titleLabel.text = title;
// 多层嵌套 ,使用多次
__weak typeof(self) weakSelf2 = strongSelf;
strongSelf.model.dataChanged = ^(NSString *title2) {
__strong typeof(self) strongSelf2 = weakSelf2;
strongSelf2.titleLabel.text = title2;
};
};