<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          iOS底層 - 銷(xiāo)毀 一個(gè) 單例

          共 5172字,需瀏覽 11分鐘

           ·

          2021-08-18 14:31


          作者 阿華12年,原文發(fā)表于掘金,點(diǎn)擊閱讀原文查看作者更多文章

          https://juejin.cn/post/6995020055356375077


          前言

          單例,我們開(kāi)發(fā)中使用很頻繁的一種設(shè)計(jì),你有沒(méi)有想過(guò),

          • 為什么其會(huì)在app生命周期中只執(zhí)行一次?

          • 系統(tǒng)底層做了哪些事情來(lái)實(shí)現(xiàn)的呢?

          • 再一點(diǎn),單例可不可以銷(xiāo)毀呢?

          帶著這些疑問(wèn),我們開(kāi)始今天的內(nèi)容。


          單例


          static dispatch_once_t onceToken;   
          dispatch_once(&onceToken, ^{ 
          class = [[self alloc] init];
          });

          單利中最重要的兩個(gè)參數(shù) 一個(gè)是 onceToken, 一個(gè)是 block。

          核心內(nèi)容有兩點(diǎn):

          • 為什么單利會(huì)調(diào)用一次?

          • 單利的這個(gè)block為什么會(huì)進(jìn)行調(diào)用?

          首先,我們先寫(xiě)一個(gè)單例來(lái)斷點(diǎn)調(diào)試看一下:

          + (instancetype)shareSM {

          static SMObject *class = nil;
          static dispatch_once_t predicate;

          NSLog(@"1:%ld", predicate);

          dispatch_once(&predicate, ^{
          NSLog(@"2:%ld", predicate);
          class = [[SMObject alloc] init];
          });

          NSLog(@"3:%ld", predicate);
          return class;
          }

          在執(zhí)行到 dispatch_once 函數(shù)的 block 中的時(shí)候,我們 bt 下看下堆棧信息:

          我們第一次調(diào)用 sharSM 方法的時(shí)候 程序執(zhí)行來(lái)到了:

          _dispatch_once_callout -> _dispatch_client_callout

          我們之前有過(guò)GCD源碼探索的經(jīng)驗(yàn),明顯這個(gè) block 是在 dispatchclient_callout 函數(shù)中調(diào)用執(zhí)行了。此處的堆棧信息也是進(jìn)一步做了驗(yàn)證。
          可以看到程序執(zhí)行的過(guò)程中, 參數(shù) predicate 的值:

          • 最開(kāi)始是 0 ;

          • block 執(zhí)行過(guò)程中 變?yōu)榱?256;

          • 最后在return 之前 變?yōu)榱?-1.

          接下來(lái),我們就看一下 diapatch_once 函數(shù)的底層源碼實(shí)現(xiàn):

          dispatch_once

          void
          dispatch_once(dispatch_once_t *val, dispatch_block_t block)
          {
          dispatch_once_f(val, block, _dispatch_Block_invoke(block));
          }

          dispatch_once_f

          void
          dispatch_once_f(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
          {
          dispatch_once_gate_t l = (dispatch_once_gate_t)val;

          #if !DISPATCH_ONCE_INLINE_FASTPATH || DISPATCH_ONCE_USE_QUIESCENT_COUNTER
          uintptr_t v = os_atomic_load(&l->dgo_once, acquire);

          // 狀態(tài)為 DLOCK_ONCE_DONE 直接返回
          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
          // 第一次進(jìn)來(lái) 獲取鎖, 原子操作多線(xiàn)程處理
          if (_dispatch_once_gate_tryenter(l)) {

          //執(zhí)行調(diào)用
          return _dispatch_once_callout(l, ctxt, func);
          }

          // 有鎖鎖住的話(huà) 會(huì) 等待開(kāi)鎖
          return _dispatch_once_wait(l);
          }

          跟著 dispatch_once_f 函數(shù)內(nèi)部流程我們解釋一下,調(diào)用一次是因?yàn)椋簝?nèi)部實(shí)現(xiàn)的 val (也就是 static dispatch_once_t predicate )底層會(huì)封裝成dispatch_once_gate_t , 這個(gè)變量用來(lái)獲取底層原子性的一個(gè)關(guān)聯(lián)。關(guān)聯(lián)一個(gè) uintptr_t 類(lèi)型 v的一個(gè)變量,用來(lái)查詢(xún)。當(dāng)前的 onceToken是一個(gè)全局的靜態(tài)變量。根據(jù)每個(gè)單利不同,每個(gè)靜態(tài)變量也不同。為了保證唯一性,在底層使用類(lèi)似KVC的形式通過(guò) os_atomic_load 出來(lái)。如果取出來(lái)的值為 DLOCK_ONCE_DONE 了:已經(jīng)處理過(guò)一次了,就retune返回出去了。當(dāng)?shù)谝淮未a執(zhí)行進(jìn)來(lái)的時(shí)候:為了保證線(xiàn)程的安全性把自己鎖起來(lái),保證當(dāng)前任務(wù)執(zhí)行的唯一,防止相同的onceToken進(jìn)行多次執(zhí)行。鎖住之后進(jìn)行 block 的調(diào)用執(zhí)行。調(diào)用完畢后將鎖解開(kāi),于此同時(shí)會(huì)將 v 的值 置為 DLOCK_ONCE_DONE(下次就不會(huì)在進(jìn)入到調(diào)用block流程)。所以保證了單利的唯一性。

          _dispatch_once_callout

          static void
          _dispatch_once_callout(dispatch_once_gate_t l, void *ctxt,
          dispatch_function_t func)
          {
          _dispatch_client_callout(ctxt, func);
          // 處理完成之后 進(jìn)行廣播
          _dispatch_once_gate_broadcast(l);
          }

          ...

          static inline void
          _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);
          }

          ...

          static inline uintptr_t
          _dispatch_once_mark_done(dispatch_once_gate_t dgo)
          {
          // 先匹配 再改變?yōu)?DLOCK_ONCE_DONE 的狀態(tài)
          // 下次 判斷為 DLOCK_ONCE_DONE 狀態(tài),就無(wú)法進(jìn)來(lái)
          return os_atomic_xchg(&dgo->dgo_once, DLOCK_ONCE_DONE, release);
          }

          _dispatch_once_gate_broadcast

          static inline void
          _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);
          }

          ...

          static inline uintptr_t
          _dispatch_once_mark_done(dispatch_once_gate_t dgo)
          {
          return os_atomic_xchg(&dgo->dgo_once, DLOCK_ONCE_DONE, release);
          }


          如何銷(xiāo)毀 ?

          我們稍微修改下單例的內(nèi)部實(shí)現(xiàn):

          + (instancetype)shareSM {

          static SMObject *class = nil;
          static dispatch_once_t predicate;

          NSLog(@"1:%ld", predicate);
          predicate = 0;
          NSLog(@"1-1 :%ld", predicate);

          dispatch_once(&predicate, ^{
          NSLog(@"2:%ld", predicate);
          class = [[SMObject alloc] init];
          });

          NSLog(@"3:%ld", predicate);
          return class;
          }

          通過(guò)日志內(nèi)容可以發(fā)現(xiàn):修改predicate 的 值 之后,每一次都會(huì)初始化一個(gè)新的對(duì)象。那么, 我們就可以通過(guò)設(shè)置predicate的值,來(lái)達(dá)到控制單例初始化次數(shù)的目的。

          那么, dealocSM 方法如何實(shí)現(xiàn)的呢?

          + (void)deallocSM {

              predicate = 0;
              class = nil;
          }

          同時(shí) , 這兩部分需要 從 shareSM 中提取出來(lái),放到類(lèi)中:

          static SMObject *class = nil;
          static dispatch_once_t predicate;

          這樣,就可以實(shí)現(xiàn)單例的銷(xiāo)毀。


          線(xiàn)程安全嗎?

          _dispatch_once_gate_tryenter(l)

          static inline bool
          _dispatch_once_gate_tryenter(dispatch_once_gate_t l)
          {
          return os_atomic_cmpxchg(&l->dgo_once, DLOCK_ONCE_UNLOCKED,
          (uintptr_t)_dispatch_lock_value_for_self(), relaxed);
          }

          這里是原子操作,對(duì)于鎖的處理,并且對(duì)線(xiàn)程操作進(jìn)行控制。_dispatch_lock_value_for_self 對(duì)于當(dāng)前自己隊(duì)列中的線(xiàn)程空間的鎖,防止多線(xiàn)程操作 。為了保證線(xiàn)程的安全性把自己鎖起來(lái),保證當(dāng)前任務(wù)執(zhí)行的唯一,防止相同的onceToken進(jìn)行多次執(zhí)行。是對(duì)多線(xiàn)程的封裝處理。


          瀏覽 55
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日a在线 日本aⅴ 日本草比 | 夜夜躁日日躁狠狠躁综合网 | 一本大道东京热无码中文字幕 | 日操夜干 | 无码1234|