block简单分类 有三种情形。
临时性的,只用在栈当中,不会存储起来。
比如数组的 foreach 遍历,这个遍历用到的block是临时的,不会存储起来。需要存储起来的,但只会调用一次,或者有一个完成周期
比如一个UIView的动画,动画完成之后,需要使用block通知外面,一旦调用block之后,这个block就可以删掉需要存储起来,可能会调用多次
比如按钮的点击事件,假如采用block实现,这种block就需要长期存储,并且会调用多次。调用之后,block也不可以删除,可能还有下一次按钮的点击。
对于临时性的,只在栈中使用的block,没有循环引用问题,block会自动释放。而只调用一次的block,需要看内部的实现,正确的实现应该是block调用之后,马上赋值为空,这样block也会释放,同样不会循环引用。
而多次调用时,block需要长期存储,就很容易出现循环引用问题。
__Block原理:
Block不允许修改外部变量的值,这里所说的外部变量的值,指的是栈中指针的内存地址。__block 所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。进而在block内部也可以修改外部变量的值。
block捕获对象
block 在实现时就会对它引用到的它所在方法中定义的栈变量进行一次只读拷贝,然后在 block 块内使用该只读拷贝;换句话说block截获自动变量的瞬时值;或者block捕获的是自动变量的副本。
由于block捕获了自动变量的瞬时值,所以在执行block语法后,即使改写block中使用的自动变量的值也不会影响block执行时自动变量的值。
weak的实现原理
weak修饰的实例变量,在实例被销毁的时候会自动置为nil,assign不会,一般修饰基本数据类型,这里重点说一下weak的实现原理。
简单来说,系统有一个全局的 CFMutableDictionary 实例,来保存每个对象的 weak 指针列表,因为每个对象可能有多个 weak 指针,所以每个对象的指针列表是 CFMutableSet 类型。
剩下我们要做的,就是在引用计数变成 0 的时候,去这个全局的字典里面,找到所有的 weak 指针,将其值设置成 nil。如何做到这一点呢?Friday QA 上介绍了一种类似 KVO 实现的方式。当对象存在 weak 指针时,我们可以将这个实例指向一个新创建的子类,然后修改这个子类的 release 方法,在 release 方法中,去从全局的 CFMutableDictionary 字典中找到所有的 weak 对象,并且设置成 nil。