昨天发了个 launch once 的 code snippet
陆续有朋友和我讨论,今天详细解释下。
launch once 这段代码实现了在一个 Engine 对象生命周期内只能调用一次 launch 方法:
而我们常用的 dispatch once 由于其使用了一个 static 变量作为 onceToken,所以会在 App 生命周期内只调用一次:
所以是这样的效果:
并不是我们所期望的。
有朋友考虑这个写法是否线程安全,其实当使用一个标记 property 的传统写法来干同样的事时,真的有考虑过线程安全么?不过也去查看了下 runtime 源码,对 association 的访问和操作都是通过内部一个 AssociationsManager C++ 类实现,而在它的实例的生命周期内,存在一把 spinlock 来保证线程安全:
因此反而还挺线程安全。昨天还讨论了有没有可能利用 dispatch_once 加上一个非 static 变量的 onceToken 来实现这个需求,比如用 self 当 onceToken。后经研究发现并不能做到,因为 dispatch_once 的实现对 onceToken 有比较苛刻的限制条件:
-
onceToken 初始值必须是 0
-
onceToken 必须是 long 类型
-
在生命周期内 onceToken 的地址不能修改,且值不能被乱改
任何不满足都会 BAD_ACCESS,所以用 self 当 onceToken 是不可行的,只可能用一个成员变量来充当,但是这已经违背了“不添加成员变量”的初衷,晚上失眠时想了好久怎么能找到个酷炫的 onceToken 来解决这个难题,结果失眠的时间更久了...
有关 dispatch_once 内部的精巧设计,可以拜读梦维的系列博文,讲的非常之深入:
http://www.dreamingwish.com/article/gcd-guide-dispatch-once-1.html
最后,还有吐槽为啥那段代码就不多打几个回车的,其实只是为了臭美用两行代码就能搞定 - -,平时用该咋写咋写啊