IMG_5795.JPG

散列表

结构图

未命名文件 (13).jpg

  • sideTables,其是StripedMap类型,本质是hash表,包含了多张sideTable的集合

    1. static StripedMap<SideTable>& SideTables() {
    2. return SideTablesMap.get();
    3. }
  • StripedMap中定义了最多能开辟8或者64张表,所以sideTable最多有8或者64张

    1. class StripedMap {
    2. #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
    3. enum { StripeCount = 8 };
    4. #else
    5. enum { StripeCount = 64 };
    6. #endif
    7. ...
    8. }
  • sideTable中包含了锁、引用计数表、弱引用表

    • spinlock_t 自旋锁
    • RefcountMap 引用计数表
    • weak_table_t 弱引用表
  • 使用sideTables多张表原因

    • 在引用计数处理过程中,经常要开表、关表,要进行加锁、解锁,如果只有一张表,性能会有消耗
    • 多张表则保证了内存能及时回收,而不是持续占有空间
    • 每个对象都开一张表,则内存过大,性能效率低

      1. struct SideTable {
      2. spinlock_t slock;
      3. RefcountMap refcnts;
      4. weak_table_t weak_table;
      5. SideTable() {
      6. memset(&weak_table, 0, sizeof(weak_table));
      7. }
      8. ~SideTable() {
      9. _objc_fatal("Do not delete SideTable.");
      10. }
      11. void lock() { slock.lock(); }
      12. void unlock() { slock.unlock(); }
      13. void forceReset() { slock.forceReset(); }
      14. // Address-ordered lock discipline for a pair of side tables.
      15. template<HaveOld, HaveNew>
      16. static void lockTwo(SideTable *lock1, SideTable *lock2);
      17. template<HaveOld, HaveNew>
      18. static void unlockTwo(SideTable *lock1, SideTable *lock2);
      19. };

      流程

  • 首先获取sideTable

  • 得到sideTable的weakTable弱引用表
  • 创建一个weak_entry_t
  • 把referent加入到weak_table的数组inline_referrers
  • 把weak_table扩容一下
  • 将new_entry加入到weak_table中

    retainCount

    获取对象的引用计数大小

  • 首先判断是否为小对象类型,如果是直接return

  • 加锁,判断isa.bits是否为nonpointerIsa
    • 旧版本中uintptr_t rc = 1 + bits.extra_rc
    • 新版本为uintptr_t rc = bits.extra_rc,因为在alloc流程时,initIsa->系统做了操作newisa.extra_rc = 1
  • 判断是否有散列表,如果有则将rc+引用计数表的一半值,上篇讲过(存在散列表时,extra_rc和散列表各存储一半值)

image.png

弱引用表

  • 运行以下代码,引用计数结果为1, 1, 2
  • 指针所指向的对象均相同,当weakObjec指向同一片堆内存时,指针个数+1,但不持有改堆对象

image.png

  • 断点调试上述43行,开启Stack Overflow,Control+单独调试,发现底层调用了objc_initWeak,此过程是LLVM做了处理,绑定到了objc_initWeak
  • 底层源码中,无论objc_initWeak还是objc_destroyWeak都是调用了storeWeak函数,区别是传入的newObj是否为nil

    • location-> __weak ptr 的地址 ```objectivec id objc_initWeak(id location, id newObj) { if (!newObj) { location = nil; return nil; }

      return storeWeak (location, (objc_object*)newObj); }

void objc_destroyWeak(id *location) { (void)storeWeak (location, nil); }

  1. <a name="WWk8K"></a>
  2. #### storeWeak
  3. - 找到散列表中的弱引用表,弱引用表中有很多对象,每个对象又有很多属性,跟关联对象结构类似,二层数据结构表
  4. - storeWeak方法实际上是接收了5个参数,分别是haveOld、haveNew和crashIfDeallocating ,这三个参数都是以模板的方式传入的,是三个bool类型的参数。 分别表示weak指针之前是否指向了一个弱引用,weak指针是否需要指向一个新的引用,若果被弱引用的对象正在析构,此时再弱引用该对象是否应该crash。
  5. - 该方法维护了oldTable 和newTable分别表示旧的引用弱表和新的弱引用表,它们都是SideTable的hash表。
  6. - 如果weak指针之前指向了一个弱引用,则会调用weak_unregister_no_lock 方法将旧的weak指针地址移除。
  7. - 如果weak指针需要指向一个新的引用,则会调用weak_register_no_lock 方法将新的weak指针地址添加到弱引用表中。
  8. - 调用setWeaklyReferenced_nolock 方法修改weak新引用的对象的bit标志位
  9. ![未命名文件 (15).jpg](https://cdn.nlark.com/yuque/0/2021/jpeg/21860440/1631171864413-7d47f43b-b927-4073-a26d-bc9dbc3ca652.jpeg#clientId=u61637c94-d0da-4&from=ui&id=u083db7b7&margin=%5Bobject%20Object%5D&name=%E6%9C%AA%E5%91%BD%E5%90%8D%E6%96%87%E4%BB%B6%20%2815%29.jpg&originHeight=1078&originWidth=1683&originalType=binary&ratio=1&size=158313&status=done&style=none&taskId=u1cebb2ff-2339-468a-b1a7-a33569e2869)
  10. <a name="Wk7jc"></a>
  11. #### weak_entry_t
  12. - 结构体类型,包含了referent(对象)和referrers(weak指针)
  13. ```objectivec
  14. struct weak_entry_t {
  15. DisguisedPtr<objc_object> referent;
  16. union {
  17. struct {
  18. weak_referrer_t *referrers;
  19. uintptr_t out_of_line_ness : 2;
  20. uintptr_t num_refs : PTR_MINUS_2;
  21. uintptr_t mask;
  22. uintptr_t max_hash_displacement;
  23. };
  24. struct {
  25. // out_of_line_ness field is low bits of inline_referrers[1]
  26. weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
  27. };
  28. };
  29. bool out_of_line() {
  30. return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
  31. }
  32. weak_entry_t& operator=(const weak_entry_t& other) {
  33. memcpy(this, &other, sizeof(other));
  34. return *this;
  35. }
  36. weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
  37. : referent(newReferent)
  38. {
  39. inline_referrers[0] = newReferrer;
  40. for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
  41. inline_referrers[i] = nil;
  42. }
  43. }
  44. };

举例

  • 以下打印结果为1 -> 1 -> 2 -> 2 -> 3

image.png

  • 断点上述45行调试,底层调用了objc_loadWeak -> objc_loadWeakRetained
  • objc_loadWeakRetained

    • 传入了location,即为weak ptr指针
    • 取*location,即对象内容
    • objc_object::rootTryRetain(),即上篇retain操作 ```objectivec id objc_loadWeakRetained(id *location) { id obj; id result; Class cls;

      SideTable *table;

    retry: // fixme std::atomic this load obj = *location; if (obj->isTaggedPointerOrNil()) return obj;

    table = &SideTables()[obj];

    table->lock(); if (*location != obj) {

    1. table->unlock();
    2. goto retry;

    }

    result = obj;

    cls = obj->ISA(); if (! cls->hasCustomRR()) {

    1. // Fast case. We know +initialize is complete because
    2. // default-RR can never be set before then.
    3. ASSERT(cls->isInitialized());
    4. if (! obj->rootTryRetain()) {
    5. result = nil;
    6. }

    } else {

    1. // Slow case. We must check for +initialize and call it outside
    2. // the lock if necessary in order to avoid deadlocks.
    3. // Use lookUpImpOrForward so we can avoid the assert in
    4. // class_getInstanceMethod, since we intentionally make this
    5. // callout with the lock held.
    6. if (cls->isInitialized() || _thisThreadIsInitializingClass(cls)) {
    7. BOOL (*tryRetain)(id, SEL) = (BOOL(*)(id, SEL))
    8. lookUpImpOrForwardTryCache(obj, @selector(retainWeakReference), cls);
    9. if ((IMP)tryRetain == _objc_msgForward) {
    10. result = nil;
    11. }
    12. else if (! (*tryRetain)(obj, @selector(retainWeakReference))) {
    13. result = nil;
    14. }
    15. }
    16. else {
    17. table->unlock();
    18. class_initialize(cls, obj);
    19. goto retry;
    20. }

    }

    table->unlock(); return result; } ```

  • weak_objc管理分离

路由器类图 (2).jpg