iOS 从源码深入探究 weak 的实现

404355009 7年前
   <h2>__weak</h2>    <pre>  <code class="language-objectivec">id __week obj1 = obj;</code></pre>    <p>编译器的模拟代码</p>    <pre>  <code class="language-objectivec">id obj1;      obj1 = 0;      objc_storeWeak(&obj1, obj);      objc_storeWeak(&obj1, 0);</code></pre>    <p>objc_storeWeak</p>    <p>函数把第二参数的赋值对象的地址作为键值,将第一参数的附有__weak修饰的变量的地址注册到weak表中。</p>    <p>如果第二参数为0,则把变量的地址从weak表中删除。</p>    <p>initWeak的实现</p>    <pre>  <code class="language-objectivec">id objc_initWeak(id *object, id value)  {      *object = 0;      return objc_storeWeak(object, value);  }</code></pre>    <p>storeWeak是Objective-C的开源部分</p>    <p>让我们来看看storeWeak到底是怎么实现的</p>    <h3>objc_storeWeak</h3>    <p>storeWeak的源码</p>    <p>官方英文注释挺全的,可以直接理解~</p>    <pre>  <code class="language-objectivec">// 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            // Set is-weakly-referenced bit in refcount table.          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.      }</code></pre>    <p>来看看storeWeak的实现</p>    <p>获取oldObj/newObj</p>    <pre>  <code class="language-objectivec">if (HaveOld) {          oldObj = *location;          oldTable = &SideTables()[oldObj];      } else {          oldTable = nil;      }      if (HaveNew) {          newTable = &SideTables()[newObj];      } else {          newTable = nil;      }</code></pre>    <p>首先是根据weak指针找到其指向的老的对象:</p>    <pre>  <code class="language-objectivec">oldObj = *location;</code></pre>    <p>然后获取到与新旧对象相关的SideTable对象:</p>    <pre>  <code class="language-objectivec">oldTable = &SideTables()[oldObj];  newTable = &SideTables()[newObj];</code></pre>    <p>&SideTables()[oldObj]这是什么鬼??</p>    <p>其时是 实现了一个类 StripedMap 重载了[]操作符</p>    <p>(c++: 哪里都能看到我 233)</p>    <pre>  <code class="language-objectivec">public:      T& operator[] (const void *p) {           return array[indexForPointer(p)].value;       }</code></pre>    <p>下面要做的就是在老对象的weak表中移除指向信息,而在新对象的weak表中建立关联信息:</p>    <pre>  <code class="language-objectivec">if (HaveOld) {      weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);  }  if (HaveNew) {      newObj = weak_register_no_lock(&newTable->weak_table, newObj,location);      // weak_register_no_lock returns NULL if weak store should be rejected  }</code></pre>    <p>接下来让弱引用指针指向新的对象:</p>    <pre>  <code class="language-objectivec">*location = newObj;</code></pre>    <p>最后会返回这个新对象:</p>    <pre>  <code class="language-objectivec">return newObj;</code></pre>    <p>以上我们能发现weak的管理实际上跟weak_table有这千丝万缕的联系,接下来就对weak_table进行分析!</p>    <p>weakTable</p>    <p>(关先上源码还是先总结...我思考了很久...。。。。)</p>    <ul>     <li>weak表是一个弱引用表,实现为一个weak_table_t结构体,存储了所有对象相关的的所有的弱引用信息</li>     <li>其中weak_entry_t是存储在弱引用表中的一个内部结构体,它负责维护和存储指向一个对象的所有弱引用hash表。</li>     <li>weak_entry_t中的referrers 存储了指向weak对象的所有变量</li>    </ul>    <p>来张图直观感受一下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/76d3fbc089340ef23e04f7e120eb7744.png"></p>    <p>下面开始对这些结构体进行分析:</p>    <ul>     <li>SideTable是一个用C++实现的类,它的具体定义在NSObject.mm中 <pre>  <code class="language-objectivec">class SideTable {  private:    static uint8_t table_buf[SIDE_TABLE_STRIPE * SIDE_TABLE_SIZE];  public:    RefcountMap refcnts;//引用计数表    weak_table_t weak_table;//弱引用表    ......  }</code></pre> </li>    </ul>    <p>weak表的结构定义:</p>    <pre>  <code class="language-objectivec">/**   * The global weak references table. Stores object ids as keys,   * and weak_entry_t structs as their values.   */  struct weak_table_t {      weak_entry_t *weak_entries;      size_t    num_entries;      uintptr_t mask;      uintptr_t max_hash_displacement;  };</code></pre>    <p>根据注释我们可以得到这是一张全局的存储object的id 和 keys的表.</p>    <p>weak_entry_t 作为他们的值.</p>    <p>来看weak_entry_t的结构体</p>    <pre>  <code class="language-objectivec">struct weak_entry_t {      DisguisedPtr<objc_object> referent;      union {          struct {              weak_referrer_t *referrers;              uintptr_t        out_of_line : 1;              uintptr_t        num_refs : PTR_MINUS_1;              uintptr_t        mask;              uintptr_t        max_hash_displacement;          };          struct {              // out_of_line=0 is LSB of one of these (don't care which)              weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];          };      };  };</code></pre>    <p>referrers: 是指向weak对象的所有变量</p>    <p>referent: 是内存上的weak对象</p>    <p>现在我们可以得出什么结论了呢</p>    <ol>     <li>OC中 弱引用变量的管理是利用 weak表(Hash表)来管理的</li>     <li>weak表中的weak_entries负责管理指向weak对象的变量</li>    </ol>    <p>weak对象的释放</p>    <p>释放对象时,废弃谁都不持有的对象的同时,程序的动作是怎么样的呢?</p>    <ul>     <li>objc_release</li>     <li>因为引用计数为0所以执行dealloc</li>     <li>objc rootDealloc</li>     <li>objc dispose</li>     <li>objc destructInstance</li>     <li>objc clear dealloctaing</li>    </ul>    <p>对象被废弃时最后调用的objc_clear_deallocating函数的动作如下:</p>    <p>(1) 从weak表中获取废弃对象的地址为键值的记录。</p>    <p>(2) 将包含在记录中的所有附有__weak修饰符变量的地址,赋值为nil</p>    <p>(3) 从weak表中删除该记录。</p>    <p>(4) 从引用计数表中删除废弃对象的地址为键值的记录。</p>    <p>由此可知,如果大量使用附有weak修饰符的变量,则会消耗相应的CPU资源。良策是只在需要避免循环引用时使用weak修饰符。</p>    <h3>立即释放对象</h3>    <pre>  <code class="language-objectivec">{      id __weak obj = [[NSObject alloc] init];  }</code></pre>    <p>因为该源码将自己生成并持有的对象赋值给附有__weak修饰符的变量中,所以自己不能持有该对象,这是会被释放并且废弃。</p>    <h3>使用附有__weak修饰符的变量,即是使用注册到autoreleasepool中的对象</h3>    <pre>  <code class="language-objectivec">{      id __weak obj1 = obj;      NSLog(@"%@",obj1);  }</code></pre>    <p>编译器模拟的代码</p>    <pre>  <code class="language-objectivec">id obj1;      objc_initWeak(&obj,obj);      id tmp = objc_loadWeakRetained(&obj);      objc_autorelease(tmp);      NSLog(@"%@", tmp);      objc_destoryWeak(&obj1);</code></pre>    <p>注意这两行代码</p>    <pre>  <code class="language-objectivec">id tmp = objc_loadWeakRetained(&obj);      objc_autorelease(tmp);</code></pre>    <p>与赋值时相比,在使用附有__weak修饰符变量的情况下,增加了对objc_loadWeakRetained函数和objc_autorelease函数的调用。</p>    <p>(1) objc_loadWeakRetained 函数取出附有__weak修饰符变量所引用的对象并retain</p>    <p>(2) objc_autorelease 函数将对象注册到autoreleasepool中。</p>    <p>注意:</p>    <p>每次使用</p>    <p>weak修饰的变量,会使变量所引用的对象注册到autoreleasepool中。</p>    <p>如果要避免这种情况可以将附有</p>    <p>weak修饰符的变量赋值给附有__strong修饰符的变量后再次使用。</p>    <pre>  <code class="language-objectivec">id __weak o = obj;  id tmp = o;</code></pre>    <h3>allowWeakReference/retainWeakReference</h3>    <p>当allowsWeakReference/retainWeakReference实例方法(没有写入NSObject接口说明文档中)返回NO的情况。</p>    <pre>  <code class="language-objectivec">- (BOOL)allowsWeakReference;  - (BOOL)retainWeakReference;</code></pre>    <p>在赋值给__weak修饰符的变量时,如果allowsWeakReference方法返回NO,程序将异常终止。</p>    <p>对象retain时,如果retainWeakReference方法返回NO, 该变量将使用nil</p>    <h3>具体源码分析</h3>    <p>以上关于weak的</p>    <ul>     <li>weak_register_no_lock</li>     <li>weak_unregister_no_lock</li>     <li>。。。<br> 很多具体实现都没有讲...<br> 我把自己看的代码加上注释贴出来了...感兴趣的可以看一下具体的实现...感受源码实现的魅力</li>    </ul>    <p>weak_unregister_no_lock</p>    <p>怎么理解呢objc_object **referrer?</p>    <pre>  <code class="language-objectivec">objc_object *referent = (objc_object *)referent_id;  objc_object **referrer = (objc_object **)referrer_id;</code></pre>    <p>我们要结合remove_referrer这个函数来理解</p>    <p>remove_referrer</p>    <pre>  <code class="language-objectivec">for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {              if (entry->inline_referrers[i] == old_referrer) {                  entry->inline_referrers[i] = nil;                  return;              }          }</code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/46bb1b8f2906177d4b5c0a447b1b16c0.png"></p>    <p>所以我们要拿到referrer 根据这个值来和entry链表中的指针进行比较,如果发现,就nil</p>    <p>(指针搞得我都晕了...佩服c/c++系统工程师)</p>    <p>(阅读源码真的是一件有意思的是哈哈)</p>    <pre>  <code class="language-objectivec">#include "objc-private.h"    #include "objc-weak.h"    #include <stdint.h>  #include <stdbool.h>  #include <sys/types.h>  #include <libkern/OSAtomic.h>    #define TABLE_SIZE(entry) (entry->mask ? entry->mask + 1 : 0)    static void append_referrer(weak_entry_t *entry, objc_object **new_referrer);    BREAKPOINT_FUNCTION(      void objc_weak_error(void)  );    /**    * Unique hash function for object pointers only.   *    * @param key The object pointer   *    * @return Size unrestricted hash of pointer.   */  static inline uintptr_t hash_pointer(objc_object *key) {      return ptr_hash((uintptr_t)key);  }    /**    * Unique hash function for weak object pointers only.   *    * @param key The weak object pointer.    *    * @return Size unrestricted hash of pointer.   */  static inline uintptr_t w_hash_pointer(objc_object **key) {      return ptr_hash((uintptr_t)key);  }    /**    * Grow the entry's hash table of referrers. Rehashes each   * of the referrers.   *    * @param entry Weak pointer hash set for a particular object.   */  __attribute__((noinline, used))  static void grow_refs_and_insert(weak_entry_t *entry,                                    objc_object **new_referrer)  {      assert(entry->out_of_line);        size_t old_size = TABLE_SIZE(entry);      size_t new_size = old_size ? old_size * 2 : 8;        size_t num_refs = entry->num_refs;      weak_referrer_t *old_refs = entry->referrers;      entry->mask = new_size - 1;        entry->referrers = (weak_referrer_t *)          calloc(TABLE_SIZE(entry), sizeof(weak_referrer_t));      entry->num_refs = 0;      entry->max_hash_displacement = 0;        for (size_t i = 0; i < old_size && num_refs > 0; i++) {          if (old_refs[i] != nil) {              append_referrer(entry, old_refs[i]);              num_refs--;          }      }      // Insert      append_referrer(entry, new_referrer);      if (old_refs) free(old_refs);  }    /**    * Add the given referrer to set of weak pointers in this entry.   * Does not perform duplicate checking (b/c weak pointers are never   * added to a set twice).    *   * @param entry The entry holding the set of weak pointers.    * @param new_referrer The new weak pointer to be added.   */  static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)  {      // if is Array implementation      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);      }        //find a place to insert ref      //weak_entry_remove() may bzero() some place      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++;  }    /**    * Remove old_referrer from set of referrers, if it's present.   * Does not remove duplicates, because duplicates should not exist.    *    * @todo this is slow if old_referrer is not present. Is this ever the case?    *   * @param entry The entry holding the referrers.   * @param old_referrer The referrer to remove.    */  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--;  }    /**    * Add new_entry to the object's table of weak references.   * Does not check whether the referent is already in the table.   */  static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry)  {      weak_entry_t *weak_entries = weak_table->weak_entries;      assert(weak_entries != nil);        //mask may keep entry in array      size_t index = hash_pointer(new_entry->referent) & (weak_table->mask);      size_t hash_displacement = 0;        //hash index 处理      while (weak_entries[index].referent != nil) {          index = (index+1) & weak_table->mask;          hash_displacement++;      }          weak_entries[index] = *new_entry;      weak_table->num_entries++;        //update max_hash_displacement      if (hash_displacement > weak_table->max_hash_displacement) {          weak_table->max_hash_displacement = hash_displacement;      }  }      static void weak_resize(weak_table_t *weak_table, size_t new_size)  {      size_t old_size = TABLE_SIZE(weak_table);        weak_entry_t *old_entries = weak_table->weak_entries;      weak_entry_t *new_entries = (weak_entry_t *)          calloc(new_size, sizeof(weak_entry_t));        weak_table->mask = new_size - 1;      //new      weak_table->weak_entries = new_entries;      weak_table->max_hash_displacement = 0;      weak_table->num_entries = 0;  // restored by weak_entry_insert below        //use pointer      if (old_entries) {          weak_entry_t *entry;          weak_entry_t *end = old_entries + old_size;          for (entry = old_entries; entry < end; entry++) {              if (entry->referent) {                  weak_entry_insert(weak_table, entry);              }          }          free(old_entries);      }  }    // Grow the given zone's table of weak references if it is full.  static void weak_grow_maybe(weak_table_t *weak_table)  {      size_t old_size = TABLE_SIZE(weak_table);        // Grow if at least 3/4 full.      if (weak_table->num_entries >= old_size * 3 / 4) {          weak_resize(weak_table, old_size ? old_size*2 : 64);      }  }    // Shrink the table if it is mostly empty.  static void weak_compact_maybe(weak_table_t *weak_table)  {      size_t old_size = TABLE_SIZE(weak_table);        // Shrink if larger than 1024 buckets and at most 1/16 full.      if (old_size >= 1024  && old_size / 16 >= weak_table->num_entries) {          weak_resize(weak_table, old_size / 8);          // leaves new table no more than 1/2 full      }  }      /**   * Remove entry from the zone's table of weak references.   */  static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry)  {      // remove entry      if (entry->out_of_line) free(entry->referrers);        //bzero()函数在由s指向的区域中放置n个0。      bzero(entry, sizeof(*entry));        weak_table->num_entries--;        //maybe resize weak_table      weak_compact_maybe(weak_table);  }      /**    * Return the weak reference table entry for the given referent.    * If there is no entry for referent, return NULL.    * Performs a lookup.   *   * @param weak_table    * @param referent The object. Must not be nil.   *    * @return The table of weak referrers to this object.    */  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];  }    /**    * Unregister an already-registered weak reference.   * This is used when referrer's storage is about to go away, but referent   * isn't dead yet. (Otherwise, zeroing referrer later would be a   * bad memory access.)   * Does nothing if referent/referrer is not a currently active weak reference.   * Does not zero referrer.   *    * FIXME currently requires old referent value to be passed in (lame)   * FIXME unregistration should be automatic if referrer is collected   *    * @param weak_table The global weak table.   * @param referent The object.   * @param referrer The weak reference.   */  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;          //after unregister the entry's referrers is empty?          // Hash implementation          if (entry->out_of_line  &&  entry->num_refs != 0) {              empty = false;          }          // Array implementation          else {              for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {                  if (entry->inline_referrers[i]) {                      empty = false;                       break;                  }              }          }          // if entry.references empty          if (empty) {              weak_entry_remove(weak_table, entry);          }      }        // Do not set *referrer = nil. objc_storeWeak() requires that the       // value not change.  }    /**    * Registers a new (object, weak pointer) pair. Creates a new weak   * object entry if it does not exist.   *    * @param weak_table The global weak table.   * @param referent The object pointed to by the weak reference.   * @param referrer The weak pointer address.   */  id   weak_register_no_lock(weak_table_t *weak_table, id referent_id,                         id *referrer_id, bool crashIfDeallocating)  {      //object      objc_object *referent = (objc_object *)referent_id;        //The Point which point the object      objc_object **referrer = (objc_object **)referrer_id;        if (!referent  ||  referent->isTaggedPointer()) return referent_id;        // ensure that the referenced object is viable      // judge is Allows Weak Reference      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 DEBUG  bool  weak_is_registered_no_lock(weak_table_t *weak_table, id referent_id)   {      return weak_entry_for_referent(weak_table, (objc_object *)referent_id);  }  #endif      /**    * Called by dealloc; nils out all weak pointers that point to the    * provided object so that they can no longer be used.   *    * @param weak_table    * @param referent The object being deallocated.    */  void   weak_clear_no_lock(weak_table_t *weak_table, id referent_id)   {        //referent objc      objc_object *referent = (objc_object *)referent_id;        //referent objc entry(which save many referents)      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;      }        //entry->referrers all nil      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);  }      /**    * This function gets called when the value of a weak pointer is being    * used in an expression. Called by objc_loadWeakRetained() which is   * ultimately called by objc_loadWeak(). The objective is to assert that   * there is in fact a weak pointer(s) entry for this particular object being   * stored in the weak-table, and to retain that object so it is not deallocated   * during the weak pointer's usage.   *    * @param weak_table    * @param referrer The weak pointer address.    */  /*    Once upon a time we eagerly cleared *referrer if we saw the referent     was deallocating. This confuses code like NSPointerFunctions which     tries to pre-flight the raw storage and assumes if the storage is     zero then the weak system is done interfering. That is false: the     weak system is still going to check and clear the storage later.     This can cause objc_weak_error complaints and crashes.    So we now don't touch the storage until deallocation completes.  */  id   weak_read_no_lock(weak_table_t *weak_table, id *referrer_id)   {      objc_object **referrer = (objc_object **)referrer_id;      objc_object *referent = *referrer;        //Detection Tagged Pointer      if (referent->isTaggedPointer()) return (id)referent;        weak_entry_t *entry;      // referent == nil or entry == nil      if (referent == nil  ||            !(entry = weak_entry_for_referent(weak_table, referent)))       {          return nil;      }      //Custom RR denotes a custom retain-release implementation      //      if (! referent->ISA()->hasCustomRR()) {          //???question          if (! referent->rootTryRetain()) {              return nil;          }      }      //has isa      else {          BOOL (*tryRetain)(objc_object *, SEL) = (BOOL(*)(objc_object *, SEL))              object_getMethodImplementation((id)referent,                                              SEL_retainWeakReference);          //IMP != _objc_magForward          if ((IMP)tryRetain == _objc_msgForward) {              return nil;          }          //IMP != nil          if (! (*tryRetain)(referent, SEL_retainWeakReference)) {              return nil;          }      }        return (id)referent;  }</code></pre>    <p> </p>    <p>来自:https://juejin.im/post/58ffe5fb5c497d0058158fee</p>    <p> </p>