循环引用
对象持有Block, Block 又捕获该对象
__weak
不会产生强引用,指向的对象销毁时,会自动将指针置为nil。因此一般通过__weak
来解决问题。
__unsafe_unretained
不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变。
使用__block
也可以解决循环引用的问题, 只是在block执行之后才会把__block
的结构体置为nil,解除循环引用
捕获对象
Block 会捕获局部变量,不会捕获全局变量,因为全局变量在哪里都可以访问,不需要捕获。
注意:为什么会捕获self
因为无论是对象方法或类方法,都会默认将self作为参数传递到方法内部,作为参数的话就是局部变量,局部变量就会被Block捕获。
访问方式:
- auto变量: 值传递 (值捕获)
- 局部static变量:指针传递 (地址捕获)
- 全局变量:直接访问 (不捕获)
内存区域变化
新创建的Block默认在数据段中,引用局部变量时会捕获变量,且Block会变为栈区;
栈区Block被copy或赋值给Strong变量后会变为堆区Block;
堆区Block被copy后类型不发生改变,引用计数增加
ARC环境下,编译器会根据情况自动将栈区的Block进行一次copy操作,将Block复制到堆上, 以下情况会进行copy操作:
- Block作为函数返回值时
- 将Block赋值给 __strong指针时
- Block作为Cocoa API中方法含有usingBlock的方法参数时
- Block作为GCD API的方法参数时
对象类型的引用
- Block捕获的变量为对象类型,会生成copy和dispose方法对内部引用的对象进行内存管理
- Block在栈上,访问对象类型auto变量时,不会对改变量产生强引用,不论Block结构体内部的变量时__strong修饰还是__weak修饰
- 如果Block被拷贝到堆上,copy函数会调用assign函数,根据auto变量的修饰符去形成强引用或弱引用
- 如果Block从堆中移除,会调用dispose函数自动释放引用的auto变量
Block内部不能修改局部变量的原因
- Block内部是值引用,拿到的只是Block内部的值,因此无法修改外部的变量
- 使用static修饰基本数据类型变量可以拿到变量地址,因此可以在Block内部修改外部变量值
- __block修饰可以解决Block内部不能修改变量值的问题,不可以用来修饰static变量和全局变量。__block将变量包装成对象,把变量封装在结构体中,Block内部存储的是结构体指针,可以通过指针找到内存地址进而修改变量的值
- 仅仅使用内存地址,而不是修改该变量的话,不用添加__block,否则系统会自动创建相应的结构体进行存储,占用不必要的内存