一. 打印一个对象
我们经常打印对象的内存地址,也从网上看到一个对象占用16个字节,那到底是不是呢?
让我们先来打印一个对象
import Foundation
//UnsafeMutableRawPointer.allocate(byteCount: <#T##Int#>, alignment: <#T##Int#>)
class LYKClass {
let age: Int = 11
}
var lykCls = LYKClass() //0x100b13090
let p1 = lykCls //0x100b13090
let p2 = lykCls //0x100b13090
print(MemoryLayout<LYKClass>.size) //8
print(MemoryLayout<LYKClass>.stride) //8
print(class_getInstanceSize(LYKClass.self)) //24 = (Swift.Object)16+Int(8)
let ptr = withUnsafeMutablePointer(to: &lykCls, {$0});
print(ptr)//0x00000001000081b8
print(ptr.pointee)//0x100b13090 ->ptr是一个指向lykCls对象的指针,ptr.pointee存的地址是lykCls的地址
使用lldb看下内存结构
我们看到有一个3好像是引用技术,有一个b是我们的age=11,看起来lykCls使用了24个字节,没有问题
(lldb) x/4wg 0x100b13090
0x100b13090: 0x0000000100008158 0x0000000600000003
0x100b130a0: 0x000000000000000b 0x000c000000000000
二. 挖一挖?
看起来和网上说的差不多,那我们往下挖一挖
看源码
1.
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
size_t requiredSize,
size_t requiredAlignmentMask) {
assert(isAlignmentMask(requiredAlignmentMask));
auto object = reinterpret_cast<HeapObject *>(
swift_slowAlloc(requiredSize, requiredAlignmentMask));
// NOTE: this relies on the C++17 guaranteed semantics of no null-pointer
// check on the placement new allocator which we have observed on Windows,
// Linux, and macOS.
new (object) HeapObject(metadata);
2.
struct HeapObject {
/// This is always a valid pointer to a metadata object.
HeapMetadata const *__ptrauth_objc_isa_pointer metadata;
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
#ifndef __swift__
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)
{ }
3.
#define SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS \
InlineRefCounts refCounts
4.
typedef RefCounts<InlineRefCountBits> InlineRefCounts;
5.
typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;
6.
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;
BitsType bits是RefCountBitsInt<refcountIsInline, sizeof(void)>的typename,所以说bits的大小=sizeof(void)=64
而RefCount
整个文件,就是对通过结构体RefCountBitOffsets
对bits进行操作。
下面是RefCountBitOffsets
的结构体元素
struct RefCountBitOffsets<8> {
/*
The bottom 32 bits (on 64 bit architectures, fewer on 32 bit) of the refcount
field are effectively a union of two different configurations:
---Normal case---
Bit 0: Does this object need to call out to the ObjC runtime for deallocation
Bits 1-31: Unowned refcount
---Immortal case---
All bits set, the object does not deallocate or have a refcount
*/
static const size_t PureSwiftDeallocShift = 0;
static const size_t PureSwiftDeallocBitCount = 1;
static const uint64_t PureSwiftDeallocMask = maskForField(PureSwiftDealloc);
static const size_t UnownedRefCountShift = shiftAfterField(PureSwiftDealloc);
static const size_t UnownedRefCountBitCount = 31;
static const uint64_t UnownedRefCountMask = maskForField(UnownedRefCount);
static const size_t IsImmortalShift = 0; // overlaps PureSwiftDealloc and UnownedRefCount
static const size_t IsImmortalBitCount = 32;
static const uint64_t IsImmortalMask = maskForField(IsImmortal);
static const size_t IsDeinitingShift = shiftAfterField(UnownedRefCount);
static const size_t IsDeinitingBitCount = 1;
static const uint64_t IsDeinitingMask = maskForField(IsDeiniting);
static const size_t StrongExtraRefCountShift = shiftAfterField(IsDeiniting);
static const size_t StrongExtraRefCountBitCount = 30;
static const uint64_t StrongExtraRefCountMask = maskForField(StrongExtraRefCount);
static const size_t UseSlowRCShift = shiftAfterField(StrongExtraRefCount);
static const size_t UseSlowRCBitCount = 1;
static const uint64_t UseSlowRCMask = maskForField(UseSlowRC);
static const size_t SideTableShift = 0;
static const size_t SideTableBitCount = 62;
static const uint64_t SideTableMask = maskForField(SideTable);
static const size_t SideTableUnusedLowBits = 3;
static const size_t SideTableMarkShift = SideTableBitCount;
static const size_t SideTableMarkBitCount = 1;
static const uint64_t SideTableMarkMask = maskForField(SideTableMark);
};
从33-62位是强引用计数,通过计算器可以看到lykCls的引用计数是3
图片.png
通过函数打印也是3
print(CFGetRetainCount(lykCls as AnyObject)) //3
三. 那弱引用呢
我们使用weak修饰p3
weak var p3 = lykCls;
通过断点我们可以看到系统创建了一个WeakReference对象,并且调用nativeInit函数,将p3的地址传了进去
WeakReference *swift::swift_weakInit(WeakReference *ref, HeapObject *value) {
ref->nativeInit(value);
return ref;
}
接下来通过p3的引用计数对象的formWeakReference创建一个HeapObjectSideTableEntry对象side
void nativeInit(HeapObject *object) {
auto side = object ? object->refCounts.formWeakReference() : nullptr;
nativeValue.store(WeakReferenceBits(side), std::memory_order_relaxed);
}
side的内部结构
class HeapObjectSideTableEntry {
std::atomic<HeapObject*> object; //包裹的对象
BitsType bits; //强引用信息
uint32_t weakBits; //弱引用信息
}
p3的内存地址
(lldb) x/3wg 0x0000000100b14000
0x100b14000: 0x0000000100008160 0xc000000020200b90
0x100b14010: 0x000000000000000b
p3的引用计数是:0xc000000020200b90,它是一个HeapObjectSideTableEntry对象的便宜地址
图片.png
将62 63位的值 置为0
图片.png
左移3位拿到0x101005C80即side的真实地址
图片.png
读取0x101005C80,看到了0x0000000600000003强引用地址,
图片.png
0x0000000100000002弱引用地址 这块不确定
网友评论