前言
- 類似于OC的源碼分析铸题,對于swift的研究迄汛,我們也從類的創(chuàng)建為出發(fā)點進(jìn)行分析蓖墅,然后分析類的底層結(jié)構(gòu)桐磁,從而了解類的本質(zhì)等等士复。
一炭懊、類的初探
我們先看看類的創(chuàng)建流程和類的大概結(jié)構(gòu)
1.0 對象的創(chuàng)建流程
對象初始化
- OC: [[HJPerosn alloc] init]尉姨,一般alloc申請內(nèi)存空間并創(chuàng)建對象庵朝,init對象進(jìn)行統(tǒng)一初始化處理。
- Swift: HJPerosn()又厉,直接()就完成了對象的創(chuàng)建九府。
開啟匯編調(diào)試:
例子一:在swift工程中,創(chuàng)建一個類繼承NSObject
- 通過斷點調(diào)試我們會發(fā)現(xiàn):這個類的創(chuàng)建流程開始走 OC的流程:__allocting_init ->objc_allocWithZone...... 原因就是HJPerson 繼承了NSObject覆致,原因是跟swift的派發(fā)機制有關(guān)侄旬,后面再深入的研究。
- 通過斷點調(diào)試我們會發(fā)現(xiàn):這個類的創(chuàng)建流程開始走 OC的流程:__allocting_init ->swift_allocObject煌妈;
-
繼續(xù)添加符號斷點:
最后:swift對象創(chuàng)建流程:
__allocating_init -> swift_allocObject -> swift_allocObject -> swift_slowAlloc ->...
在VSCode中的源碼里儡羔,根據(jù)此流程逐漸對其源碼進(jìn)行分析如下:
1.1 swift_allocObject 源碼分析
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));//分配內(nèi)存+字節(jié)對齊
//注意:這依賴于c++ 17保證的無空指針語義
//檢查我們在Windows上觀察到的新分配器的位置,
// Linux和macOS璧诵。
new (object) HeapObject(metadata);//初始化一個實例對象
//如果啟用了泄漏跟蹤笔链,請開始跟蹤此對象。
SWIFT_LEAKS_START_TRACKING_OBJECT(object);
SWIFT_RT_TRACK_INVOCATION(object, swift_allocObject);
return object;
}
-
swift_allocObject
的源碼如下腮猖,主要有以下幾部分- 通過
swift_slowAlloc
分配內(nèi)存鉴扫,并進(jìn)行內(nèi)存字節(jié)對齊 - 通過
new + HeapObject + metadata
初始化一個實例對象 - 函數(shù)的返回值是
HeapObject
類型,所以當(dāng)前對象的內(nèi)存結(jié)構(gòu)
就是HeapObject
的內(nèi)存結(jié)構(gòu)
- 通過
1.2 swift_allocObject 源碼分析
void *swift::swift_slowAlloc(size_t size, size_t alignMask) {
void *p;
//這個檢查也強制“默認(rèn)”對齊使用AlignedAlloc澈缺。 if (alignMask <= MALLOC_ALIGN_MASK) {
#if defined(__APPLE__)
p = malloc_zone_malloc(DEFAULT_ZONE(), size);
#else
p = malloc(size);// 堆中創(chuàng)建size大小的內(nèi)存空間坪创,用于存儲實例變量
#endif
} else {
size_t alignment = (alignMask == ~(size_t(0)))
? _swift_MinAllocationAlignment
: alignMask + 1;
p = AlignedAlloc(size, alignment);
}
if (!p) swift::crash("Could not allocate memory.");
return p;
}
- 進(jìn)入
swift_slowAlloc
函數(shù),其內(nèi)部主要是通過malloc_zone_malloc
在堆中分配size大小的內(nèi)存空間姐赡,并返回內(nèi)存地址莱预,主要是用于存儲實例變量
1.3 查看HeapObject 并 計算類的大小
// The members of the HeapObject header that are not shared by a
// standard Objective-C instance
#define SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS \
InlineRefCounts refCounts ///引用計數(shù)
/// The Swift heap-object header.
/// This must match RefCountedStructTy in IRGen.
struct HeapObject {
/// This is always a valid pointer to a metadata object.
HeapMetadata const *metadata;/// 元數(shù)據(jù)
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS; ///引用計數(shù)
-
metadata
:是 HeapMetedata類型 -
refCounts
: 引用計數(shù)
【總結(jié)】
- Swift中實例對象,默認(rèn)的比OC中多了一個refCounted引用計數(shù)大小项滑,默認(rèn)屬性占16字節(jié) : metadata(struct)8字節(jié)和refCounts(class)8字節(jié)
- OC中實例對象的本質(zhì)是結(jié)構(gòu)體依沮,是以objc_object為模板繼承的,其中有一個isa指針枪狂,占8字節(jié)
1.4【驗證+拓展】
//驗證
class HJPerson {
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print(class_getInstanceSize(HJPerson.self))
}
}
打印 : 16
//拓展
class HJPerson {
var age : Int = 20
var name : String = "HJ"
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print(class_getInstanceSize(HJPerson.self))
}
}
打印 : 40
驗證的確一個空類的大小為16危喉;那么為啥第二個是40呢?
我們通過打印Int 和 String 內(nèi)存大小來驗證
//********* Int底層定義 *********
@frozen public struct Int : FixedWidthInteger, SignedInteger {...}
//String底層定義
@frozen public struct String {...}
//驗證
print(MemoryLayout<Int>.stride)
print(MemoryLayout<String>.stride)
打印
8
16
從打印的結(jié)果中可以看出州疾,Int類型占8字節(jié)辜限,String類型占16字節(jié),這點與OC中是有所區(qū)別的 严蓖,以后再進(jìn)行詳細(xì)講解吧薄嫡。
所以這也解釋了為什么HJPerson的內(nèi)存大小等于40氧急,即40 = metadata(8字節(jié)) +refCount(8字節(jié))+ Int(8字節(jié))+ String(16字節(jié))
二、Swift中類的結(jié)構(gòu)探索
- 在OC中類是從
objc_class
模板繼承過來的毫深,可參考: iOS 底層探索:類的結(jié)構(gòu)分析
- 在Swift中吩坝,類的結(jié)構(gòu)在底層是
HeapObject
,其中有metadata
+refCounts
2.1 metadata的底層探索
- 進(jìn)入HeapMetadata定義哑蔫,是TargetHeapMetaData類型的別名钾恢,接收了一個參數(shù)Inprocess
using HeapMetadata = TargetHeapMetaData<Inprocess>;
- 進(jìn)入TargetHeapMetaData定義,其本質(zhì)是一個模板類型鸳址,其中定義了一些所需的數(shù)據(jù)結(jié)構(gòu)瘩蚪。這個結(jié)構(gòu)體中沒有屬性,只有初始化方法稿黍,傳入了一個MetadataKind類型的參數(shù)(該結(jié)構(gòu)體沒有疹瘦,那么只有在父類中了)這里的kind就是傳入的Inprocess
//模板類型
template <typename Runtime>
struct TargetHeapMetadata : TargetMetadata<Runtime> {
using HeaderType = TargetHeapMetadataHeader<Runtime>;
TargetHeapMetadata() = default;
//初始化方法
constexpr TargetHeapMetadata(MetadataKind kind)
: TargetMetadata<Runtime>(kind) {}
};
- 進(jìn)入
TargetMetaData
定義,有一個kind屬性巡球,kind的類型就是之前傳入的Inprocess言沐。從這里可以得出,對于kind酣栈,其類型就是unsigned long险胰,主要用于區(qū)分是哪種類型的元數(shù)據(jù)
// TargetMetaData 定義
struct TargetMetaData{
using StoredPointer = typename Runtime: StoredPointer;
...
StoredPointer kind;
}
// Inprocess 定義
struct Inprocess{
...
using StoredPointer = uintptr_t;
...
}
//******** uintptr_t 定義 ********
typedef unsigned long uintptr_t;
- 可以看出初始化方法中參數(shù)
kind
的類型是MetadataKind
,
2.2 getClassObject
- 回到TargetMetaData結(jié)構(gòu)體定義中,找方法getClassObject
const TargetClassMetadata<Runtime> *getClassObject() const;
//******** 具體實現(xiàn) ********
template<> inline const ClassMetadata *
Metadata::getClassObject() const {
//匹配kind
switch (getKind()) {
//如果kind是class
case MetadataKind::Class: {
// Native Swift class metadata is also the class object.
//將當(dāng)前指針強轉(zhuǎn)為ClassMetadata類型
return static_cast<const ClassMetadata *>(this);
}
case MetadataKind::ObjCClassWrapper: {
// Objective-C class objects are referenced by their Swift metadata wrapper.
auto wrapper = static_cast<const ObjCClassWrapperMetadata *>(this);
return wrapper->Class;
}
// Other kinds of types don't have class objects.
default:
return nullptr;
}
}
- 在該方法中去匹配
kind
返回值是TargetClassMetadata
類型
通過lldb
來驗證
po metadata->getKind()
矿筝,得到其kind是Classpo metadata->getClassObject()
起便、x/8g 0x0000000110efdc70,這個地址中存儲的是元數(shù)據(jù)
信息!
所以TargetMetadata
和 TargetClassMetadata
本質(zhì)上是一樣的窖维,因為在內(nèi)存結(jié)構(gòu)中榆综,可以直接進(jìn)行指針的轉(zhuǎn)換,所以可以說铸史,我們認(rèn)為的結(jié)構(gòu)體鼻疮,其實就是TargetClassMetadata
2.3 TargetClassMetadata
進(jìn)入TargetClassMetadata
定義,繼承自TargetAnyClassMetadata
琳轿,有以下這些屬性判沟,這也是類結(jié)構(gòu)的部分
template <typename Runtime>
struct TargetClassMetadata : public TargetAnyClassMetadata<Runtime> {
...
//swift特有的標(biāo)志
ClassFlags Flags;
//實力對象內(nèi)存大小
uint32_t InstanceSize;
//實例對象內(nèi)存對齊方式
uint16_t InstanceAlignMask;
//運行時保留字段
uint16_t Reserved;
//類的內(nèi)存大小
uint32_t ClassSize;
//類的內(nèi)存首地址
uint32_t ClassAddressPoint;
...
}
- 當(dāng)前類返回的實際類型是 TargetClassMetadata,而TargetMetaData中只有一個屬性kind,TargetAnyClassMetaData中有3個屬性崭篡,分別是kind挪哄, superclass,cacheData
- 當(dāng)前Class在內(nèi)存中所存放的屬性由 TargetClassMetadata屬性 + TargetAnyClassMetaData屬性 + TargetMetaData屬性 構(gòu)成媚送,所以得出的metadata的數(shù)據(jù)結(jié)構(gòu)體如下所示
struct swift_class_t: NSObject{
void *kind;//相當(dāng)于OC中的isa中燥,kind的實際類型是unsigned long
void *superClass;
void *cacheData;
void *data;
uint32_t flags; //4字節(jié)
uint32_t instanceAddressOffset;//4字節(jié)
uint32_t instanceSize;//4字節(jié)
uint16_t instanceAlignMask;//2字節(jié)
uint16_t reserved;//2字節(jié)
uint32_t classSize;//4字節(jié)
uint32_t classAddressOffset;//4字節(jié)
void *description;
...
}
三寇甸、分析思路整理
四塘偎、swif與OC 類的結(jié)構(gòu)對比
-
實例對象 & 類
OC中的實例對象本質(zhì)是結(jié)構(gòu)體疗涉,是通過底層的
objc_object
模板創(chuàng)建,類是繼承自objc_class
Swift中的實例對象本質(zhì)也是結(jié)構(gòu)體吟秩,類型是
HeapObject
咱扣,比OC多了一個refCounts
-
引用計數(shù)
OC中的ARC維護(hù)的是
散列表
Swift中的ARC是對象內(nèi)部有一個
refCounts
屬性
- 方法列表
OC中的方法存儲在
objc_class
結(jié)構(gòu)體class_rw_t
的methodList
中swift中的方法存儲在
metadata
元數(shù)據(jù)中