dispatch_once我们平时经常用吧,特别是创建单例的时候,但是可能我们平时只是在于用它,并不知道他是如何实现的。
我也在思考这个问题。
+(instancetype)shareInstance{
static ServiceApp *service = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
service = [[ServiceApp alloc]init];
});
return service;
}
单例是如何只执行一次的呢?
带着这个问题,我们往下看。
这个函数的基本内部构成,我们主要看这个函数dispatch_once_f。
#ifdef __BLOCKS__
__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0)
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
//注意这个内联函数
DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
_dispatch_once(dispatch_once_t *predicate, dispatch_block_t block)
{
if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) {
dispatch_once(predicate, block);
}
}
#undef dispatch_once
#define dispatch_once _dispatch_once
#endif
__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0)
DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW
void
dispatch_once_f(dispatch_once_t *predicate, void *context,
dispatch_function_t function);
DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_NONNULL1 DISPATCH_NONNULL3
DISPATCH_NOTHROW
void
_dispatch_once_f(dispatch_once_t *predicate, void *context,
dispatch_function_t function)
{
if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) {
dispatch_once_f(predicate, context, function);
}
}
#undef dispatch_once_f
#define dispatch_once_f _dispatch_once_f
__END_DECLS
#endif
下面我们来看dispatch_once_f。
dispatch_once_f(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
{
//结构体
dispatch_once_gate_t l = (dispatch_once_gate_t)val;
//这里我们放到后面去看,请记住DLOCK_ONCE_DONE这个东西
#if !DISPATCH_ONCE_INLINE_FASTPATH || DISPATCH_ONCE_USE_QUIESCENT_COUNTER
uintptr_t v = os_atomic_load(&l->dgo_once, acquire);
if (likely(v == DLOCK_ONCE_DONE)) {
return;
}
#if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
if (likely(DISPATCH_ONCE_IS_GEN(v))) {
return _dispatch_once_mark_done_if_quiesced(l, v);
}
#endif
#endif
if (_dispatch_once_gate_tryenter(l)) {
return _dispatch_once_callout(l, ctxt, func);
}
return _dispatch_once_wait(l);
通过查阅资料,dispatch_once_gate_t是一个结构体。
我们来看这个函数。
if (_dispatch_once_gate_tryenter(l)) {
return _dispatch_once_callout(l, ctxt, func);
}
_dispatch_once_gate_tryenter(dispatch_once_gate_t l)
{
// os 对象是否存储过
// unlock
return os_atomic_cmpxchg(&l->dgo_once, DLOCK_ONCE_UNLOCKED,
(uintptr_t)_dispatch_lock_value_for_self(), relaxed);
}
dispatch_once_callout是执行dispatch_once block里的函数。
_dispatch_once_callout(dispatch_once_gate_t l, void *ctxt,
dispatch_function_t func)
{
_dispatch_client_callout(ctxt, func);
_dispatch_once_gate_broadcast(l);
}
执行完_dispatch_client_callout这个函数,就执行下面的广播函数_dispatch_once_gate_broadcast(l) 。
_dispatch_once_gate_broadcast(dispatch_once_gate_t l)
{
dispatch_lock value_self = _dispatch_lock_value_for_self();
uintptr_t v;
#if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
v = _dispatch_once_mark_quiescing(l);
#else
v = _dispatch_once_mark_done(l);
#endif
if (likely((dispatch_lock)v == value_self)) return;
_dispatch_gate_broadcast_slow(&l->dgo_gate, (dispatch_lock)v);
}
我们主要看_dispatch_once_mark_quiescing和_dispatch_once_mark_done这两个地方。
_dispatch_once_mark_quiescing(dispatch_once_gate_t dgo)
{
return os_atomic_xchg(&dgo->dgo_once, _dispatch_once_generation(), release);
}
_dispatch_once_mark_quiescing标记着正在创建。
_dispatch_once_mark_done(dispatch_once_gate_t dgo)
{
return os_atomic_xchg(&dgo->dgo_once, DLOCK_ONCE_DONE, release);
}
_dispatch_once_mark_done标识结束,并标记为DLOCK_ONCE_DONE。
_dispatch_once_mark_done(dispatch_once_gate_t dgo)
{
return os_atomic_xchg(&dgo->dgo_once, DLOCK_ONCE_DONE, release);
}
在这里我们可以回头看,最开始dispatch_once_f中的代码。
uintptr_t v = os_atomic_load(&l->dgo_once, acquire);
if (likely(v == DLOCK_ONCE_DONE)) {
return;
}
#if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
if (likely(DISPATCH_ONCE_IS_GEN(v))) {
return _dispatch_once_mark_done_if_quiesced(l, v);
}
相信看到DLOCK_ONCE_DONE,就应该懂了,是什么。如果检测到已经执行过,就直接返回。
如果是正在创建,就执行_dispatch_once_mark_done_if_quiesced,并标记为DLOCK_ONCE_DONE。
_dispatch_once_mark_done_if_quiesced(dispatch_once_gate_t dgo, uintptr_t gen)
{
if (_dispatch_once_generation() - gen >= DISPATCH_ONCE_GEN_SAFE_DELTA) {
/*
* See explanation above, when the quiescing counter approach is taken
* then this store needs only to be relaxed as it is used as a witness
* that the required barriers have happened.
*/
os_atomic_store(&dgo->dgo_once, DLOCK_ONCE_DONE, relaxed);
}
}
dispatch_once的原理大概就是这样了。为什么只执行一次。
主要在DLOCK_ONCE_DONE这个标记。
网友评论