Swift内存管理初探

HeapObject

在Swift中,一个Class对象实际上就是一个HeapObject结构体指针。那么它的内存布局是怎样的呢? 首先我们先来看一下 HeapObject 的结构:

struct HeapObject {
  /// This is always a valid pointer to a metadata object.
  HeapMetadata const *metadata;

  SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;

  HeapObject() = default;

  // Initialize a HeapObject header as appropriate for a newly-allocated object.
  constexpr HeapObject(HeapMetadata const *newMetadata) 
    : metadata(newMetadata)
    , refCounts(InlineRefCounts::Initialized)
  { }

  // Initialize a HeapObject header for an immortal object
  constexpr HeapObject(HeapMetadata const *newMetadata,
                       InlineRefCounts::Immortal_t immortal)
  : metadata(newMetadata)
  , refCounts(InlineRefCounts::Immortal)
  { }

};

从中我们可以看到:
第一个字段是一个 Metadata 对象,该对象有着与OC中 isa_t 类似的作用,用来描述对象类型的(等价于 type(of:) 取得的结果)! 但本文的重点不是在这,我们继续往下看!

这才是本文的主角哦: SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS (这是一个宏定义) !

我们点开这个宏来看看这是个啥?

RefCounts refCounts;

这就是Swift中引用计数 refCounts了,引用计数、弱引用、unowned 引用都与它有关!

继续深挖定义refCounts的这个类 InlineRefCounts

typedef RefCounts InlineRefCounts;
typedef RefCounts SideTableRefCounts

InlineRefCounts: 是用在常规对象以及无主引用中
SideTableRefCounts: 则是用在weak引用对象中

InlineRefCounts 被起了个别名为 RefCounts,我们看继续看下 RefCounts类结构

template     
class RefCounts {
  std::atomic refCounts;
  ....
}

这里省略了所有的方法和类型定义! 注意: 这是模版类,那么就要注意传进来的模版参数

首先我们来看看 InlineRefCountBits (常规对象)

typedef RefCountBitsT InlineRefCountBits;

template 
class RefCountBitsT {

  friend class RefCountBitsT;
  friend class RefCountBitsT;

  static const RefCountInlinedness Inlinedness = refcountIsInline;

  typedef typename RefCountBitsInt::Type
    BitsType;
  typedef typename RefCountBitsInt::SignedType
    SignedBitsType;
  typedef RefCountBitOffsets
    Offsets;

  BitsType bits;

  // ...

};

通过模板替换之后,InlineRefCountBits 实际上就是一个 uint64_t! 位图如下:


占位@2x.png

接下来我们在看看 SideTableRefCounts (weak引用)

首先我们先来写一段代码,看看能从汇编中发现什么?

class People {
    var age = 10
}

let p = People()
weak var p2 = p
汇编@2x.png

咦,这个swift_weakInit是什么? 它又做了什么? 我们继续往里看

WeakReference *swift::swift_weakInit(WeakReference *ref, HeapObject *value) {
  ref->nativeInit(value);
  return ref;
}

从上述代码可以看出该对象为WeakReference类! 那么 weak 变量, 编译器则会在声明的过程中就定义了WeakReference对象,该对象主要目的就是用来管理当前的弱引用对象!
该对象调用了其中的 nativeInit(value) 方法;传递了当前的HeapObject对象

继续看 nativeInit 具体操作

void nativeInit(HeapObject *object) {
    auto side = object ? object->refCounts.formWeakReference() : nullptr;
    nativeValue.store(WeakReferenceBits(side), std::memory_order_relaxed);
}

如果当前对象不为空则调用object->refCounts.formWeakReference()方法、我们查看下 formWeakReference方法的实现

template <> HeapObjectSideTableEntry* RefCounts::formWeakReference()
{
  auto side = allocateSideTable(true);
  if (side)
    return side->incrementWeak();
  else
    return nullptr;
}

创建SideTable、如果创建成功,则执行 incrementWeak();
查看allocateSideTable创建过程

template <>
HeapObjectSideTableEntry* RefCounts::allocateSideTable(bool failIfDeiniting)
{
  auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
  // Preflight failures before allocating a new side table.
  if (oldbits.hasSideTable()) {
    // Already have a side table. Return it.
    return oldbits.getSideTable();
  } 
  else if (failIfDeiniting && oldbits.getIsDeiniting()) {
    // Already past the start of deinit. Do nothing.
    return nullptr;
  }
  HeapObjectSideTableEntry *side = new HeapObjectSideTableEntry(getHeapObject());
  auto newbits = InlineRefCountBits(side);
  ....
  return side;
}
  • 首先拿到原有的引用计数 refCounts.load(SWIFT_MEMORY_ORDER_CONSUME)
  • 然后通过 getHeapObject() 创建一个 HeapObjectSideTableEntry 实例对象 side
  • 再把创建出来的 side对象地址给 InlineRefCountBits 、而InlineRefCountBits 就是我们分析强引用计数的 RefCountBitsT!

顺便一起来看一下 HeapObjectSideTableEntry 的结构:

class HeapObjectSideTableEntry {
  std::atomic object;
  SideTableRefCounts refCounts;
  public:
  HeapObjectSideTableEntry(HeapObject *newObject)
    : object(newObject), refCounts()
  { }
  ......
}

在HeapObjectSideTableEntry中保留了原对象的Metadata和refCound!
还有bits(强引用、无主引用的信息) 以及新增的 weakBits(弱引用信息)

小结

swift对象都是以HeapObject为模板创建!
RefCounts:
第一种是 InlineRefCounts ,用在 常规对象(无主) 中,它其实是一个 uint64_t!
第二种是SideTableRefCounts, 用在weak对象中,此时它是一个指向 SideTable(HeapObjectSideTableEntry) 的指针。

注意点: OC与Swift区别

OC:
弱引用计数是存放在全局维护的散列表中,isa中会记录是否使用了散列表。
在引用计数为0时,自动触发dealloc,会检查并清空当前对象的散列表计数。

Swift
弱引用计数也是存放在散列表中,这个散列表是局部的! 每个weak对象都有自己的散列表!
即便一个对象在使用了弱引用后,也不能保证相关内存全部被释放! 因为SideTable 的生命周期与对象是分离的,当强引用计数为 0 时,只有 HeapObject 被释放了,但不会触发SideTable的清空。而是在下次访问时发现当前对象为nil时,才会清空SideTable

你可能感兴趣的:(Swift内存管理初探)