欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

iOS 中weak的實(shí)現(xiàn)代碼示例

 更新時(shí)間:2018年05月03日 14:05:07   作者:滿臉胡茬的小碼農(nóng)  
本篇文章主要介紹了iOS 中weak的實(shí)現(xiàn)代碼示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

只要學(xué)過(guò) iOS 的人,都會(huì)對(duì) strong、weak、copy等關(guān)鍵字應(yīng)該都會(huì)很熟悉。weak 屬性關(guān)鍵字就是弱引用,它不會(huì)增加引用計(jì)數(shù)但卻能保證指針的安全訪問(wèn),在對(duì)象釋放后置為 nil,從而避免錯(cuò)誤的內(nèi)存訪問(wèn)。主要為了解決循環(huán)引用的問(wèn)題。

接下來(lái),我們會(huì)從 objc 庫(kù)中的 NSObject.mm、 objc-weak.h 以及 objc-weak.mm 文件出發(fā),去具體了解 weak 的實(shí)現(xiàn)過(guò)程。

weak 的內(nèi)部結(jié)構(gòu)

Runtime 維護(hù)了一個(gè)weak表,用于存儲(chǔ)指向某個(gè)對(duì)象的所有weak指針。weak 表是由單個(gè)自旋鎖管理的散列表。
weak表其實(shí)是一個(gè)hash表,key 是所指對(duì)象的指針,value是weak指針的地址(這個(gè)地址的值是所指向?qū)ο蟮牡刂罚?shù)組。

在下面涉及的源碼中,我們會(huì)看到以下幾個(gè)類型:

sideTable、weak_table_t、weak_entry_t 這幾個(gè)結(jié)構(gòu)體。

struct SideTable {
  // 自旋鎖,用來(lái)保證線程安全
  spinlock_t slock;
  // 引用計(jì)數(shù)表
  RefcountMap refcnts;
  // weak 表
  weak_table_t weak_table;

  ...
};

SideTable,它用來(lái)管理引用計(jì)數(shù)表和 weak 表,并使用 spinlock_lock 自旋鎖來(lái)防止操作表結(jié)構(gòu)時(shí)可能的競(jìng)態(tài)條件。它用一個(gè) 64*128 大小的uint8_t 靜態(tài)數(shù)組作為 buffer 來(lái)保存所有的 SideTable 實(shí)例。這個(gè)結(jié)構(gòu)體里面包含三個(gè)變量,第一個(gè)spinlock_t,它是一個(gè)自旋鎖,用來(lái)保證線程安全。第二個(gè)RefcountMap,是引用計(jì)數(shù)表,每個(gè)對(duì)象的引用計(jì)數(shù)保存在全局的引用計(jì)數(shù)表中,一個(gè)對(duì)象地址對(duì)應(yīng)一個(gè)引用計(jì)數(shù)。第三個(gè)就是我們接下來(lái)要講的 weak 表,所有的 weak 變量會(huì)被加入到全局的weak表中,表的 key 是 weak 修飾的變量指向的對(duì)象, value 值就是 weak 修飾的變量。接下來(lái),我們具體看看這個(gè) weak 表

struct weak_table_t {
  // 保存了所有指向指定對(duì)象的 weak 指針
  weak_entry_t *weak_entries;
  // 存儲(chǔ)空間,即 entries 的數(shù)目
  size_t  num_entries;
  // 參與判斷引用計(jì)數(shù)輔助量
  uintptr_t mask;
  // hash key 最大偏移量
  uintptr_t max_hash_displacement;
};

這個(gè)是全局弱引用的 hash 表。它的作用就是在對(duì)象執(zhí)行 dealloc 的時(shí)候?qū)⑺兄赶蛟搶?duì)象的 weak 指針的值設(shè)為 nil, 避免懸空指針。它使用不定類型對(duì)象的地址的 hash 化后的數(shù)值作為 key,用 weak_entry_t 類型的結(jié)構(gòu)體對(duì)象作為 value。其中 weak_entry_t 是存儲(chǔ)在弱引用表中的一個(gè)內(nèi)部結(jié)構(gòu)體,它負(fù)責(zé)維護(hù)和存儲(chǔ)指向一個(gè)對(duì)象的所有弱引用 hash 表。其定義如下:

// 存儲(chǔ)在弱引用表中的一個(gè)內(nèi)部結(jié)構(gòu)體
#define WEAK_INLINE_COUNT 4
struct weak_entry_t {
  DisguisedPtr<objc_object> referent;         // 封裝 objc_object 指針,即 weak 修飾的變量指向的對(duì)象
  union {
    struct {
      weak_referrer_t *referrers;
      uintptr_t    out_of_line : 1;      // LSB 最低有效元 當(dāng)標(biāo)志位為0時(shí),增加引用表指針緯度,
                            // 當(dāng)其為0的時(shí)候, weak_referrer_t 成員將擴(kuò)展為靜態(tài)數(shù)組型的 hash table
      uintptr_t    num_refs : PTR_MINUS_1;  // 引用數(shù)值,這里記錄弱引用表中引用有效數(shù)字,即里面元素的數(shù)量
      uintptr_t    mask;
      uintptr_t    max_hash_displacement;   // hash 元素上限閥值
    };
    struct {
      // out_of_line=0 is LSB of one of these (don't care which)
      weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];   
    };
  };
};

在 weak_entry_t 的結(jié)構(gòu)中, DisguisedPtr<objc_object> 是對(duì) objc_object * 指針及其一些操作進(jìn)行的封裝,目的就是為了讓它給人看起來(lái)不會(huì)有內(nèi)存泄露的樣子,其內(nèi)容可以理解為對(duì)象的內(nèi)存地址。out_of-line 成員為最低有效位,當(dāng)其為 0 的時(shí)候,weak_referrer_t 成員將擴(kuò)展為一個(gè)靜態(tài)數(shù)組型的 hash table。其實(shí) weak_referrer 是objc_objcet 的別名,定義如下:typedef objc_object ** weak_referrer_t;

它通過(guò)一個(gè)二維指針地址偏移,用下標(biāo)作為 hash 的 key,做成了一個(gè)弱引用散列。

每個(gè)對(duì)象的 SideTable 中的 weak_table_t 都是全局 weak 表的入口,以引用計(jì)數(shù)對(duì)象為鍵找到其所記錄的 weak 修飾的對(duì)象。weak_entry_t 中的 referrers 有兩種形式,當(dāng) out_of_line 為 0 的時(shí)候,referrers 是一個(gè)靜態(tài)數(shù)組型的表,數(shù)組大小默認(rèn)為 WEAK_INLINE_COUNT 大小,當(dāng) out_of_line 不為 0 的時(shí)候,referrers 是一個(gè)動(dòng)態(tài)數(shù)組,內(nèi)容隨之增加。

weak 實(shí)現(xiàn)原理的過(guò)程

當(dāng)我們用 weak 修飾屬性的時(shí)候,它是怎么實(shí)現(xiàn)當(dāng)所引用的對(duì)象被廢棄的時(shí)候,變量置為 nil,我們來(lái)探究一下。

{
  id obj1 = [[NSObject alloc] init];
  id __weak obj2 = obj1;
}

經(jīng)過(guò)編譯期轉(zhuǎn)換之后,以上代碼會(huì)變成下面這樣

id obj2;
objc_initWeak(&obj2, obj1);
objc_destroyWeak(&obj2);

我們發(fā)現(xiàn),weak 修飾符變量是通過(guò) objc_initWeak 函數(shù)來(lái)初始化的,在變量作用域結(jié)束的時(shí)候通過(guò) objc_destroyWeak 函數(shù)來(lái)釋放該變量的。接下來(lái),我們看看這兩個(gè)函數(shù)的源碼。

id objc_initWeak(id *location, id newObj)
{
  // 查看對(duì)象實(shí)例是否有效
  // 無(wú)效對(duì)象直接導(dǎo)致指針釋放
  if (!newObj) {
    *location = nil;
    return nil;
  }
  // 這里傳遞了三個(gè) bool 數(shù)值
  // 使用 template 進(jìn)行常量參數(shù)傳遞是為了優(yōu)化性能
  return storeWeak<false/*old*/, true/*new*/, true/*crash*/>
    (location, (objc_object*)newObj);
}
void objc_destroyWeak(id *location)
{
  (void)storeWeak<true/*old*/, false/*new*/, false/*crash*/>
    (location, nil);
}

對(duì)這兩個(gè)方法的分析后,我們發(fā)現(xiàn)它們都調(diào)用了storeWeak 這個(gè)函數(shù),但是兩個(gè)方法傳入的參數(shù)卻稍有不同。

init 方法中,第一個(gè)參數(shù)為 weak 修飾的變量,第二個(gè)參數(shù)為引用計(jì)數(shù)對(duì)象。但在 destoryWeak 函數(shù),第一參數(shù)依舊為 weak 修飾的變量,第二個(gè)參數(shù)為 nil。那這塊傳入不同的參數(shù)到底代表什么,我們繼續(xù)分析 storeWeak 這個(gè)函數(shù)。

// 更新一個(gè)弱引用變量
// 如果 HaveOld 是 true, 變量是個(gè)有效值,需要被及時(shí)清理。變量可以為 nil。
// 如果 HaveNew 是 true, 需要一個(gè)新的 value 來(lái)替換變量。變量可以為 nil
// 如果crashifdeallocation 是 ture ,那么如果 newObj 是 deallocating,或者 newObj 的類不支持弱引用,則該進(jìn)程就會(huì)停止。
// 如果crashifdeallocation 是 false,那么 nil 會(huì)被存儲(chǔ)。

template <bool HaveOld, bool HaveNew, bool CrashIfDeallocating>
static id storeWeak(id *location, objc_object *newObj)
{
  assert(HaveOld || HaveNew);
  if (!HaveNew) assert(newObj == nil);

  Class previouslyInitializedClass = nil;
  id oldObj;
  
  // 創(chuàng)建新舊散列表
  SideTable *oldTable;
  SideTable *newTable;

  // Acquire locks for old and new values.
  // 獲得新值和舊值的鎖存位置 (用地址作為唯一標(biāo)示)
  // Order by lock address to prevent lock ordering problems.
  // 通過(guò)地址來(lái)建立索引標(biāo)志,防止桶重復(fù)
  // Retry if the old value changes underneath us.
  // 下面指向的操作會(huì)改變舊值
 retry:
  if (HaveOld) {
  // 如果 HaveOld 為 true ,更改指針,獲得以 oldObj 為索引所存儲(chǔ)的值地址
    oldObj = *location;
    oldTable = &SideTables()[oldObj];
  } else {
    oldTable = nil;
  }
  if (HaveNew) {
  // 獲得以 newObj 為索引所存儲(chǔ)的值對(duì)象
    newTable = &SideTables()[newObj];
  } else {
    newTable = nil;
  }
  
// 對(duì)兩個(gè) table 進(jìn)行加鎖操作,防止多線程中競(jìng)爭(zhēng)沖突
  SideTable::lockTwo<HaveOld, HaveNew>(oldTable, newTable);

// location 應(yīng)該與 oldObj 保持一致,如果不同,說(shuō)明當(dāng)前的 location 已經(jīng)處理過(guò) oldObj 可是又被其他線程所修改, 保證線程安全,這個(gè)判斷用來(lái)避免線程沖突重處理問(wèn)題
  if (HaveOld && *location != oldObj) {
    SideTable::unlockTwo<HaveOld, HaveNew>(oldTable, newTable);
    goto retry;
  }

  // Prevent a deadlock between the weak reference machinery
  // and the +initialize machinery by ensuring that no 
  // weakly-referenced object has an un-+initialized isa.
  // 防止弱引用之間發(fā)生死鎖,并且通過(guò) +initialize 初始化構(gòu)造器保證所有弱引用的 isa 非空指向
  if (HaveNew && newObj) {
    // 獲得新對(duì)象的 isa 指針
    Class cls = newObj->getIsa();
    // 判斷 isa 非空且已經(jīng)初始化
    if (cls != previouslyInitializedClass && 
      !((objc_class *)cls)->isInitialized()) 
    {
      // 對(duì)兩個(gè)表解鎖
      SideTable::unlockTwo<HaveOld, HaveNew>(oldTable, newTable);
      _class_initialize(_class_getNonMetaClass(cls, (id)newObj));

      // If this class is finished with +initialize then we're good.
      // If this class is still running +initialize on this thread 
      // (i.e. +initialize called storeWeak on an instance of itself)
      // then we may proceed but it will appear initializing and 
      // not yet initialized to the check above.
      // Instead set previouslyInitializedClass to recognize it on retry.
      // 如果該類已經(jīng)完成執(zhí)行 +initialize 方法是最好的,如果該類 + initialize 在線程中,例如 +initialize 正在調(diào)用storeWeak 方法,那么則需要手動(dòng)對(duì)其增加保護(hù)策略,并設(shè)置 previouslyInitializedClass 指針進(jìn)行標(biāo)記然后重新嘗試
      previouslyInitializedClass = cls;

      goto retry;
    }
  }

  // Clean up old value, if any. 清除舊值
  if (HaveOld) {
    weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
  }

  // Assign new value, if any. 分配新值
  if (HaveNew) {
    newObj = (objc_object *)weak_register_no_lock(&newTable->weak_table, 
                           (id)newObj, location, 
                           CrashIfDeallocating);
    // weak_register_no_lock returns nil if weak store should be rejected
    // 如果弱引用被釋放則該方法返回 nil
    // Set is-weakly-referenced bit in refcount table.
    // 在引用計(jì)數(shù)表中設(shè)置弱引用標(biāo)記位
    if (newObj && !newObj->isTaggedPointer()) {
      newObj->setWeaklyReferenced_nolock();
    }

    // Do not set *location anywhere else. That would introduce a race.
    *location = (id)newObj;
  }
  else {
    // No new value. The storage is not changed.
  }
  
  SideTable::unlockTwo<HaveOld, HaveNew>(oldTable, newTable);

  return (id)newObj;
}

以上就是 store_weak 這個(gè)函數(shù)的實(shí)現(xiàn),它主要做了以下幾件事:

  1. 聲明了新舊散列表指針,因?yàn)?weak 修飾的變量如果之前已經(jīng)指向一個(gè)對(duì)象,然后其再次改變指向另一個(gè)對(duì)象,那么按理來(lái)說(shuō)我們需要釋放舊對(duì)象中該 weak 變量的記錄,也就是要將舊記錄刪除,然后在新記錄中添加。這里的新舊散列表就是這個(gè)作用。
  2. 根據(jù)新舊變量的地址獲取相應(yīng)的 SideTable
  3. 對(duì)兩個(gè)表進(jìn)行加鎖操作,防止多線程競(jìng)爭(zhēng)沖突
  4. 進(jìn)行線程沖突重處理判斷
  5. 判斷其 isa 是否為空,為空則需要進(jìn)行初始化
  6. 如果存在舊值,調(diào)用 weak_unregister_no_lock 函數(shù)清除舊值
  7. 調(diào)用 weak_register_no_lock 函數(shù)分配新值
  8. 解鎖兩個(gè)表,并返回第二參數(shù)

初始化弱引用對(duì)象流程一覽

弱引用的初始化,從上文的分析可以看出,主要的操作部分就是在弱引用表的取鍵、查詢散列、創(chuàng)建弱引用等操作,可以總結(jié)出如下的流程圖:


舊對(duì)象解除注冊(cè)操作 weak_unregister_no_lock

void weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, 
            id *referrer_id)
{
  objc_object *referent = (objc_object *)referent_id;
  objc_object **referrer = (objc_object **)referrer_id;

  weak_entry_t *entry;

  if (!referent) return;

  if ((entry = weak_entry_for_referent(weak_table, referent))) {
    remove_referrer(entry, referrer);
    bool empty = true;
    if (entry->out_of_line && entry->num_refs != 0) {
      empty = false;
    }
    else {
      for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
        if (entry->inline_referrers[i]) {
          empty = false; 
          break;
        }
      }
    }

    if (empty) {
      weak_entry_remove(weak_table, entry);
    }
  }

  // Do not set *referrer = nil. objc_storeWeak() requires that the 
  // value not change.
}

該方法主要作用是將舊對(duì)象在 weak_table 中接觸 weak 指針的對(duì)應(yīng)綁定。根據(jù)函數(shù)名,稱之為解除注冊(cè)操作。

來(lái)看看這個(gè)函數(shù)的邏輯。首先參數(shù)是 weak_table_t 表,鍵和值。聲明 weak_entry_t 變量,如果key,也就是引用計(jì)數(shù)對(duì)象為空,直接返回。根據(jù)全局入口表和鍵獲取對(duì)應(yīng)的 weak_entry_t 對(duì)象,也就是 weak 表記錄。獲取到記錄后,將記錄表以及 weak 對(duì)象作為參數(shù)傳入 remove_referrer 函數(shù)中,這個(gè)函數(shù)就是解除操作。然后判斷這個(gè) weak 記錄是否為空,如果為空,從全局記錄表中清除相應(yīng)的引用計(jì)數(shù)對(duì)象的 weak 記錄表。

接下來(lái),我們了解一下,如何獲取這個(gè) weak_entry_t 這個(gè)變量。

static weak_entry_t *weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
  assert(referent);

  weak_entry_t *weak_entries = weak_table->weak_entries;

  if (!weak_entries) return nil;

  size_t index = hash_pointer(referent) & weak_table->mask;
  size_t hash_displacement = 0;
  while (weak_table->weak_entries[index].referent != referent) {
    index = (index+1) & weak_table->mask;
    hash_displacement++;
    if (hash_displacement > weak_table->max_hash_displacement) {
      return nil;
    }
  }

  return &weak_table->weak_entries[index];
}

這個(gè)函數(shù)的邏輯就是先獲取全局 weak 表入口,然后將引用計(jì)數(shù)對(duì)象的地址進(jìn)行 hash 化后與 weak_table->mask 做與操作,作為下標(biāo),在全局 weak 表中查找,若找到,返回這個(gè)對(duì)象的 weak 記錄表,若沒(méi)有,返回nil。

再來(lái)了解一下解除對(duì)象的函數(shù):

static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
{
  if (! entry->out_of_line) {
    for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
      if (entry->inline_referrers[i] == old_referrer) {
        entry->inline_referrers[i] = nil;
        return;
      }
    }
    _objc_inform("Attempted to unregister unknown __weak variable "
           "at %p. This is probably incorrect use of "
           "objc_storeWeak() and objc_loadWeak(). "
           "Break on objc_weak_error to debug.\n", 
           old_referrer);
    objc_weak_error();
    return;
  }

  size_t index = w_hash_pointer(old_referrer) & (entry->mask);
  size_t hash_displacement = 0;
  while (entry->referrers[index] != old_referrer) {
    index = (index+1) & entry->mask;
    hash_displacement++;
    if (hash_displacement > entry->max_hash_displacement) {
      _objc_inform("Attempted to unregister unknown __weak variable "
             "at %p. This is probably incorrect use of "
             "objc_storeWeak() and objc_loadWeak(). "
             "Break on objc_weak_error to debug.\n", 
             old_referrer);
      objc_weak_error();
      return;
    }
  }
  entry->referrers[index] = nil;
  entry->num_refs--;
}

這個(gè)函數(shù)傳入的是 weak 對(duì)象,當(dāng) out_of_line 為0 時(shí),遍歷數(shù)組,找到對(duì)應(yīng)的對(duì)象,置nil,如果未找到,報(bào)錯(cuò)并返回。當(dāng) out_of_line 不為0時(shí),根據(jù)對(duì)象的地址 hash 化并和 mask 做與操作作為下標(biāo),查找相應(yīng)的對(duì)象,若沒(méi)有,報(bào)錯(cuò)并返回,若有,相應(yīng)的置為 nil,并減少元素?cái)?shù)量,即 num_refs 減 1。

新對(duì)象添加注冊(cè)操作 weak_register_no_lock

id weak_register_no_lock(weak_table_t *weak_table, id referent_id, 
           id *referrer_id, bool crashIfDeallocating)
{
  objc_object *referent = (objc_object *)referent_id;
  objc_object **referrer = (objc_object **)referrer_id;

  if (!referent || referent->isTaggedPointer()) return referent_id;

  // ensure that the referenced object is viable
  bool deallocating;
  if (!referent->ISA()->hasCustomRR()) {
    deallocating = referent->rootIsDeallocating();
  }
  else {
    BOOL (*allowsWeakReference)(objc_object *, SEL) = 
      (BOOL(*)(objc_object *, SEL))
      object_getMethodImplementation((id)referent, 
                      SEL_allowsWeakReference);
    if ((IMP)allowsWeakReference == _objc_msgForward) {
      return nil;
    }
    deallocating =
      ! (*allowsWeakReference)(referent, SEL_allowsWeakReference);
  }

  if (deallocating) {
    if (crashIfDeallocating) {
      _objc_fatal("Cannot form weak reference to instance (%p) of "
            "class %s. It is possible that this object was "
            "over-released, or is in the process of deallocation.",
            (void*)referent, object_getClassName((id)referent));
    } else {
      return nil;
    }
  }

  // now remember it and where it is being stored
  weak_entry_t *entry;
  if ((entry = weak_entry_for_referent(weak_table, referent))) {
    append_referrer(entry, referrer);
  } 
  else {
    weak_entry_t new_entry;
    new_entry.referent = referent;
    new_entry.out_of_line = 0;
    new_entry.inline_referrers[0] = referrer;
    for (size_t i = 1; i < WEAK_INLINE_COUNT; i++) {
      new_entry.inline_referrers[i] = nil;
    }

    weak_grow_maybe(weak_table);
    weak_entry_insert(weak_table, &new_entry);
  }

  // Do not set *referrer. objc_storeWeak() requires that the 
  // value not change.

  return referent_id;
}

一大堆 if-else, 主要是為了判斷該對(duì)象是不是 taggedPoint 以及是否正在調(diào)用 dealloca 等。下面操作開(kāi)始,同樣是先獲取 weak 表記錄,如果獲取到,則調(diào)用 append_referrer 插入對(duì)象,若沒(méi)有,則新建一個(gè) weak 表記錄,默認(rèn)為 out_of_line,然后將新對(duì)象放到 0 下標(biāo)位置,其他位置置為 nil 。下面兩個(gè)函數(shù) weak_grow_maybe 是用來(lái)判斷是否需要重申請(qǐng)內(nèi)存重 hash,weak_entry_insert 函數(shù)是用來(lái)將新建的 weak 表記錄插入到全局 weak 表中。插入時(shí)同樣是以對(duì)象地址的 hash 化和 mask 值相與作為下標(biāo)來(lái)記錄的。

接下來(lái)看看 append_referrer 函數(shù),源代碼如下:

static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
  if (! entry->out_of_line) {
    // Try to insert inline.
    for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
      if (entry->inline_referrers[i] == nil) {
        entry->inline_referrers[i] = new_referrer;
        return;
      }
    }

    // Couldn't insert inline. Allocate out of line.
    weak_referrer_t *new_referrers = (weak_referrer_t *)
      calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
    // This constructed table is invalid, but grow_refs_and_insert
    // will fix it and rehash it.
    for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
      new_referrers[i] = entry->inline_referrers[I];
    }
    entry->referrers = new_referrers;
    entry->num_refs = WEAK_INLINE_COUNT;
    entry->out_of_line = 1;
    entry->mask = WEAK_INLINE_COUNT-1;
    entry->max_hash_displacement = 0;
  }

  assert(entry->out_of_line);

  if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
    return grow_refs_and_insert(entry, new_referrer);
  }
  size_t index = w_hash_pointer(new_referrer) & (entry->mask);
  size_t hash_displacement = 0;
  while (entry->referrers[index] != NULL) {
    index = (index+1) & entry->mask;
    hash_displacement++;
  }
  if (hash_displacement > entry->max_hash_displacement) {
    entry->max_hash_displacement = hash_displacement;
  }
  weak_referrer_t &ref = entry->referrers[index];
  ref = new_referrer;
  entry->num_refs++;
}

當(dāng) out_of_line 為 0,并且靜態(tài)數(shù)組里面還有位置存放,那么直接存放并返回。如果沒(méi)有位置存放,則升級(jí)為動(dòng)態(tài)數(shù)組,并加入。如果 out_of_line 不為 0,先判斷是否需要擴(kuò)容,然后同樣的,使用對(duì)象地址的 hash 化和 mask 做與操作作為下標(biāo),找到相應(yīng)的位置并插入。

對(duì)象的銷(xiāo)毀以及 weak 的置 nil 實(shí)現(xiàn)

釋放時(shí),調(diào)用clearDeallocating函數(shù)。clearDeallocating 函數(shù)首先根據(jù)對(duì)象地址獲取所有weak指針地址的數(shù)組,然后遍歷這個(gè)數(shù)組把其中的數(shù)據(jù)設(shè)為nil,最后把這個(gè)entry從weak表中刪除,最后清理對(duì)象的記錄。

當(dāng)weak引用指向的對(duì)象被釋放時(shí),又是如何去處理weak指針的呢?當(dāng)釋放對(duì)象時(shí),其基本流程如下:

  1. 調(diào)用 objc_release
  2. 因?yàn)閷?duì)象的引用計(jì)數(shù)為0,所以執(zhí)行dealloc
  3. 在dealloc 中,調(diào)用了_objc_rootDealloc 函數(shù)
  4. 在 _objc_rootDealloc 中,調(diào)用了 objec_dispose 函數(shù)
  5. 調(diào)用objc_destructInstance
  6. 最后調(diào)用 objc_clear_deallocating

objc_clear_deallocating的具體實(shí)現(xiàn)如下:

void objc_clear_deallocating(id obj) 
{
  assert(obj);
  assert(!UseGC);

  if (obj->isTaggedPointer()) return;
  obj->clearDeallocating();
}

這個(gè)函數(shù)只是做一些判斷以及更深層次的函數(shù)調(diào)用,

void objc_object::sidetable_clearDeallocating()
{
  SideTable& table = SideTables()[this];

  // clear any weak table items
  // clear extra retain count and deallocating bit
  // (fixme warn or abort if extra retain count == 0 ?)
  table.lock();
  // 迭代器
  RefcountMap::iterator it = table.refcnts.find(this);
  if (it != table.refcnts.end()) {
    if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
      weak_clear_no_lock(&table.weak_table, (id)this);
    }
    table.refcnts.erase(it);
  }
  table.unlock();
}

我們可以看到,在這個(gè)函數(shù)中,首先取出對(duì)象對(duì)應(yīng)的SideTable實(shí)例,如果這個(gè)對(duì)象有關(guān)聯(lián)的弱引用,則調(diào)用weak_clear_no_lock來(lái)清除對(duì)象的弱引用信息,我們?cè)趤?lái)深入一下,

void weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
{
  objc_object *referent = (objc_object *)referent_id;

  weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
  if (entry == nil) {
    /// XXX shouldn't happen, but does with mismatched CF/objc
    //printf("XXX no entry for clear deallocating %p\n", referent);
    return;
  }

  // zero out references
  weak_referrer_t *referrers;
  size_t count;

  if (entry->out_of_line) {
    referrers = entry->referrers;
    count = TABLE_SIZE(entry);
  } 
  else {
    referrers = entry->inline_referrers;
    count = WEAK_INLINE_COUNT;
  }

  for (size_t i = 0; i < count; ++i) {
    objc_object **referrer = referrers[I];
    if (referrer) {
      if (*referrer == referent) {
        *referrer = nil;
      }
      else if (*referrer) {
        _objc_inform("__weak variable at %p holds %p instead of %p. "
               "This is probably incorrect use of "
               "objc_storeWeak() and objc_loadWeak(). "
               "Break on objc_weak_error to debug.\n", 
               referrer, (void*)*referrer, (void*)referent);
        objc_weak_error();
      }
    }
  }

  weak_entry_remove(weak_table, entry);
}

這個(gè)函數(shù)根據(jù) out_of_line 的值,取得對(duì)應(yīng)的記錄表,然后根據(jù)引用計(jì)數(shù)對(duì)象,將相應(yīng)的 weak 對(duì)象置 nil。最后清除相應(yīng)的記錄表。

通過(guò)上面的描述,我們基本能了解一個(gè)weak引用從生到死的過(guò)程。從這個(gè)流程可以看出,一個(gè)weak引用的處理涉及各種查表、添加與刪除操作,還是有一定消耗的。所以如果大量使用__weak變量的話,會(huì)對(duì)性能造成一定的影響。那么,我們應(yīng)該在什么時(shí)候去使用weak呢?《Objective-C高級(jí)編程》給我們的建議是只在避免循環(huán)引用的時(shí)候使用__weak修飾符。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • iOS用AutoLayout實(shí)現(xiàn)分頁(yè)滾動(dòng)功能

    iOS用AutoLayout實(shí)現(xiàn)分頁(yè)滾動(dòng)功能

    這篇文章主要給大家介紹了關(guān)于iOS用AutoLayout實(shí)現(xiàn)分頁(yè)滾動(dòng)功能的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位iOS開(kāi)發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • iOS實(shí)現(xiàn)圖片自動(dòng)切換效果

    iOS實(shí)現(xiàn)圖片自動(dòng)切換效果

    這篇文章主要為大家詳細(xì)介紹了iOS實(shí)現(xiàn)圖片自動(dòng)切換效果,類似android畫(huà)廊效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • iOS Crash常規(guī)跟蹤方法及Bugly集成運(yùn)用詳細(xì)介紹

    iOS Crash常規(guī)跟蹤方法及Bugly集成運(yùn)用詳細(xì)介紹

    這篇文章主要介紹了iOS Crash常規(guī)跟蹤方法及Bugly集成運(yùn)用詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • iOS中TableView如何統(tǒng)一數(shù)據(jù)源代理詳解

    iOS中TableView如何統(tǒng)一數(shù)據(jù)源代理詳解

    這篇文章主要給大家介紹了關(guān)于iOS中TableView如何統(tǒng)一數(shù)據(jù)源代理的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07
  • iOS性能優(yōu)化淺析

    iOS性能優(yōu)化淺析

    給大家通過(guò)理論和經(jīng)驗(yàn)分析了IOS性能優(yōu)化各方面的問(wèn)題,以及處理的對(duì)應(yīng)辦法,有需要的朋友參考下。
    2018-02-02
  • iOS實(shí)現(xiàn)UIButton的拖拽功能

    iOS實(shí)現(xiàn)UIButton的拖拽功能

    這篇文章主要為大家詳細(xì)介紹了iOS實(shí)現(xiàn)UIButton的拖拽功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • iOS像素對(duì)齊概念解析

    iOS像素對(duì)齊概念解析

    這篇文章主要為大家詳細(xì)介紹了iOS像素對(duì)齊概念,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • iOS微信分享后關(guān)閉發(fā)送成功提示并返回應(yīng)用

    iOS微信分享后關(guān)閉發(fā)送成功提示并返回應(yīng)用

    這篇文章主要為大家詳細(xì)介紹了iOS微信分享后關(guān)閉發(fā)送成功提示并返回應(yīng)用的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • iOS使用AFN進(jìn)行單圖和多圖上傳的實(shí)例代碼

    iOS使用AFN進(jìn)行單圖和多圖上傳的實(shí)例代碼

    本篇文章中主要介紹了iOS使用AFN進(jìn)行單圖和多圖上傳的實(shí)例代碼,整理出單張和多張圖片上傳的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-04-04
  • iOS App開(kāi)發(fā)中使用及自定義UITableViewCell的教程

    iOS App開(kāi)發(fā)中使用及自定義UITableViewCell的教程

    這篇文章主要介紹了iOS App開(kāi)發(fā)中使用及自定義UITableViewCell的教程,自定義TableViewCell文中使用Objective-C演示而非ib,需要的朋友可以參考下
    2016-04-04

最新評(píng)論