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<InlineRefCountBits> refCounts;
这就是Swift中引用计数 refCounts了,引用计数、弱引用、unowned 引用都与它有关!
继续深挖定义refCounts的这个类 InlineRefCounts
typedef RefCounts<InlineRefCountBits> InlineRefCounts;
typedef RefCounts<SideTableRefCountBits> SideTableRefCounts
InlineRefCounts: 是用在常规对象以及无主引用中
SideTableRefCounts: 则是用在weak引用对象中
InlineRefCounts 被起了个别名为 RefCounts,我们看继续看下 RefCounts类结构
template <typename RefCountBits>
class RefCounts {
std::atomic<RefCountBits> refCounts;
....
}
这里省略了所有的方法和类型定义! 注意: 这是模版类,那么就要注意传进来的模版参数
首先我们来看看 InlineRefCountBits (常规对象)
typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;
template <RefCountInlinedness refcountIsInline>
class RefCountBitsT {
friend class RefCountBitsT<RefCountIsInline>;
friend class RefCountBitsT<RefCountNotInline>;
static const RefCountInlinedness Inlinedness = refcountIsInline;
typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::Type
BitsType;
typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::SignedType
SignedBitsType;
typedef RefCountBitOffsets<sizeof(BitsType)>
Offsets;
BitsType bits;
// ...
};
通过模板替换之后,InlineRefCountBits 实际上就是一个 uint64_t! 位图如下:
接下来我们在看看 SideTableRefCounts (weak引用)
首先我们先来写一段代码,看看能从汇编中发现什么?
class People {
var age = 10
}
let p = People()
weak var p2 = p
咦,这个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<InlineRefCountBits>::formWeakReference()
{
auto side = allocateSideTable(true);
if (side)
return side->incrementWeak();
else
return nullptr;
}
创建SideTable、如果创建成功,则执行 incrementWeak();
查看allocateSideTable创建过程
template <>
HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::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<HeapObject*> 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