1.類(lèi)與結(jié)構(gòu)體
1.1 類(lèi)
class PersonClass {
//1.類(lèi)中可以定義存儲(chǔ)值的屬性
var age : Int = 18
var name : String = "小明"
// 2.類(lèi)中可以定義方法
func test() {
print("%@多少%@歲", self.name, self.age);
}
// 3.類(lèi)中可以定義初始化器
// 指定初始化器 默認(rèn)只有一個(gè)
init(_ age: Int, _ name: String) {
self.age = age
self.name = name
}
// 便捷初始化器 必須從相同的類(lèi)里調(diào)用另一個(gè)初始化器
// 安全性能原則:self及其屬性已經(jīng)初始化了
convenience init(_ age: Int) {
self.init(18, "小明")
self.age = age
self.name = "小王"
}
// 可失敗初始化
init?(age: Int, name: String) {
if age < 18 {return nil}
self.age = age
self.name = name
}
// 必要初始化器 所有該類(lèi)的子類(lèi)都必須 實(shí)現(xiàn)該初始化器
required init(age1: Int, name1:String) {
self.age = age1
self.name = name1
}
// 類(lèi)有析構(gòu)函數(shù), 結(jié)構(gòu)體沒(méi)有
deinit {
}
}
class SubPersonClass: PersonClass {
var subName: String
// 子類(lèi)的所有屬性必須在父類(lèi)初始化之前初始化完成
// 子類(lèi)初始化器必須先向上委托父類(lèi)初始化器,然后才能為繼承的屬性設(shè)置新值。如果不這樣做,指定初始化器賦予的新值將被父類(lèi)中的初始化器所覆蓋
// 確保成員變量訪問(wèn)安全
init(_ subName : String) {
self.subName = subName;
super.init(18, "小明")
}
}
1.2 結(jié)構(gòu)體
// 結(jié)構(gòu)體
struct PersonStruct {
//1.結(jié)構(gòu)體中可以定義存儲(chǔ)值的屬性
var age : Int = 18
var name : String = "小明"
// 2.結(jié)構(gòu)體中可以定義方法
func test() {
print("%@多少%@歲", self.name, self.age);
}
// 3.結(jié)構(gòu)體中可以定義初始化器
// 與類(lèi)不同,編譯器會(huì)提供默認(rèn)的初始化方法(前提是我們自己沒(méi)有指定初始化器)
init(age : Int, name : String) {
self.age = age
self.name = name
}
}
1.3 類(lèi)和結(jié)構(gòu)體的相同點(diǎn)
- 定義存儲(chǔ)值的屬性
- 定義方法
- 定義下標(biāo)以使用語(yǔ)法提供對(duì)其值的訪問(wèn)
- 定義初始化器
- 使用
extension來(lái)拓展功能 - 遵循協(xié)議來(lái)提供某種功能
1.4 類(lèi)和結(jié)構(gòu)體的不同點(diǎn)
- 類(lèi)有繼承的特性,而結(jié)構(gòu)體沒(méi)有
- 類(lèi)型轉(zhuǎn)換使您能在運(yùn)行時(shí)檢查和解釋class的實(shí)例對(duì)象的類(lèi)型
- 類(lèi)有析構(gòu)函數(shù)用來(lái)釋放其占用的資源, 而結(jié)構(gòu)體沒(méi)有
- 類(lèi)有引用計(jì)數(shù),允許對(duì)一個(gè)類(lèi)實(shí)例有多個(gè)引用
- 類(lèi)是引用類(lèi)型,結(jié)構(gòu)體是值類(lèi)型
2. 引用類(lèi)型和值類(lèi)型
- 引用類(lèi)型的變量并不直接存儲(chǔ)具體的實(shí)例對(duì)象,而是對(duì)當(dāng)前存儲(chǔ)具體實(shí)例內(nèi)存地址的引用。
- 值類(lèi)型的變量存儲(chǔ)的就是具體的實(shí)例(或者說(shuō)具體的值)
- 引用類(lèi)型存儲(chǔ)在
堆上 - 值類(lèi)型存儲(chǔ)在
棧上
借助幾個(gè)LLDB指令來(lái)查看當(dāng)前變量的內(nèi)存結(jié)構(gòu)
- po 只會(huì)輸出對(duì)應(yīng)的值
- p 則會(huì)輸出返回值的類(lèi)型以及命令結(jié)果
- x/8g: 讀取內(nèi)存中的值(8g: 8字節(jié)格式輸出)
- frame varibale -L xxx:查看各實(shí)例內(nèi)存信息
2.1 引用類(lèi)型
引用賦值給var、let或者給函數(shù)傳參,是將內(nèi)存地址拷貝一份。類(lèi)似于制作一個(gè)文件的替身(快捷方式、鏈接),指向的是同一個(gè)文件。屬于淺拷貝

p和p1都引用了同一個(gè)PersonClass的實(shí)例地址

p和p1在內(nèi)存中的地址相差8個(gè)字節(jié)(存儲(chǔ)著當(dāng)前實(shí)例對(duì)象的內(nèi)存地址)

2.2 值類(lèi)型
值類(lèi)型賦值給 var、let或者給函數(shù)傳參,是直接將所有內(nèi)容拷貝一份。類(lèi)似于對(duì)文件進(jìn)行復(fù)制粘貼操作,產(chǎn)生了全新的文件副本。屬于深拷貝。

ps和ps1是內(nèi)存當(dāng)中存儲(chǔ)的值,并且更改ps1當(dāng)中的age或name對(duì)ps沒(méi)有影響,因?yàn)?code>ps和ps1存儲(chǔ)的是兩個(gè)不同的值

3.類(lèi)的生命周期
iOS開(kāi)發(fā)的語(yǔ)言不管是OC還是Swift后端都是通過(guò)LLVM進(jìn)行編譯的,如下圖所示:

OC通過(guò)clang 編譯器,編譯成IR,然后再生成可執(zhí)行文件 .o(這里也就是我們的機(jī)器碼)而
Swift則是通過(guò)Swift編譯器編輯成IR,然后再生成可執(zhí)行文件,具體編譯過(guò)程如下:
swift code經(jīng)過(guò)-dump-parse的命令來(lái)進(jìn)行語(yǔ)法分析,解析成我們的AST(也就是抽象語(yǔ)法樹(shù));- 通過(guò)
-dump-ast語(yǔ)義分析來(lái)檢查我們的語(yǔ)義,(例如類(lèi)型檢查是否準(zhǔn)確安全);- 完成之后
Swift代碼將會(huì)降級(jí)為SIL,也就是Swift中間語(yǔ)言/代碼(Swift intermediate language);SIL分為Raw SIL(原生的,沒(méi)有開(kāi)啟優(yōu)化選項(xiàng)) 和SILOpt Canonical SIL(經(jīng)過(guò)優(yōu)化的);- 最后通過(guò)
LLVM降級(jí)為IR,然后通過(guò)后端代碼編譯為不同架構(gòu)的機(jī)器碼
我們也可以通過(guò)swiftc命令去生成對(duì)應(yīng)的文件
// 分析輸出AST
swiftc main.swift -dump-parse
// 分析并且檢查類(lèi)型輸出AST
swiftc main.swift -dump-ast
// 生成中間體語(yǔ)言(SIL),未優(yōu)化
swiftc main.swift -emit-silgen
// 生成中間體語(yǔ)言(SIL),優(yōu)化后的
swiftc main.swift -emit-sil
// 生成LLVM中間體語(yǔ)言 (.ll文件)
swiftc main.swift -emit-ir
// 生成LLVM中間體語(yǔ)言 (.bc文件)
swiftc main.swift -emit-bc
// 生成匯編
swiftc main.swift -emit-assembly
// 編譯生成可執(zhí)行.out文件
swiftc -o main.o main.swift
SIL文件中的重要標(biāo)識(shí)可參考:SIL官方文檔
接下來(lái)開(kāi)始分析類(lèi)的初始化
class Person {
var age : Int = 18
var name : String = "小明"
}
var p = Person()
通過(guò)swiftc main.swift -emit-sil > ./main.sil生成.sil文件,在.sil文件中 注意到__allocating_init()創(chuàng)建實(shí)例對(duì)象的函數(shù)
// Person.__allocating_init()
sil hidden [exact_self_class] @$s4main6PersonCACycfC : $@convention(method) (@thick Person.Type) -> @owned Person {
// %0 "$metatype"
bb0(%0 : $@thick Person.Type):
%1 = alloc_ref $Person // user: %3
// function_ref Person.init()
%2 = function_ref @$s4main6PersonCACycfc : $@convention(method) (@owned Person) -> @owned Person // user: %3
%3 = apply %2(%1) : $@convention(method) (@owned Person) -> @owned Person // user: %4
return %3 : $Person // id: %4
} // end sil function '$s4main6PersonCACycfC'
其中Person.Type元類(lèi)型的作用類(lèi)似于 OC 里的isa
alloc_ref根據(jù)官方文檔中的解釋
Allocates an object of reference type T. The object will be initialized with retain count 1; its state will be otherwise uninitialized. The optional objc attribute indicates that the object should be allocated using Objective-C's allocation methods (+allocWithZone:)(可以理解為去堆區(qū)申請(qǐng)內(nèi)存空間)
進(jìn)入?yún)R編我們可以看到__allocating_init的調(diào)用

進(jìn)入到
__allocating_init內(nèi)部
可以看到
__allocating_init內(nèi)部調(diào)用了一個(gè)函數(shù)swift_allocObject通過(guò) Swift源碼 中的
HeapObject.cpp文件可以發(fā)現(xiàn)
HeapObject *swift::swift_allocObject(HeapMetadata const *metadata,
size_t requiredSize,
size_t requiredAlignmentMask) {
CALL_IMPL(swift_allocObject, (metadata, requiredSize, requiredAlignmentMask));
}
SWIFT_RUNTIME_EXPORT
HeapObject *(*SWIFT_RT_DECLARE_ENTRY _swift_allocObject)(
HeapMetadata const *metadata, size_t requiredSize,
size_t requiredAlignmentMask) = _swift_allocObject_;
swift_allocObject走的是內(nèi)部函數(shù)_swift_allocObject_,進(jìn)入到_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));
// 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);
// If leak tracking is enabled, start tracking this object.
SWIFT_LEAKS_START_TRACKING_OBJECT(object);
SWIFT_RT_TRACK_INVOCATION(object, swift_allocObject);
return object;
}
跳轉(zhuǎn)至swift_slowAlloc
void *swift::swift_slowAlloc(size_t size, size_t alignMask) {
void *p;
// This check also forces "default" alignment to use AlignedAlloc.
if (alignMask <= MALLOC_ALIGN_MASK) {
#if defined(__APPLE__) && SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC
p = malloc_zone_malloc(DEFAULT_ZONE(), size);
#else
p = malloc(size);
#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;
}
因此我們可以看出Swift對(duì)象的內(nèi)存分配流程如下
__allocating_init--->swift_allocObject ---> _ swift_allocObject ---> swift_slowAlloc--->Malloc
在_swift_allocObject_函數(shù)里我們可以看到返回的是HeapObject類(lèi)型,因此Swift對(duì)象內(nèi)存結(jié)構(gòu)為HeapObject
進(jìn)入到HeapObject
/// 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 *__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)
{ }
#ifndef NDEBUG
void dump() const SWIFT_USED;
#endif
#endif // __swift__
};
我們可以看到HeapObject有兩個(gè)屬性,默認(rèn)占用 16 字節(jié)大?。?/p>
- 一個(gè)是
HeapMatadata類(lèi)型的metadata,8字節(jié),- 一個(gè)是
refCount,8字節(jié)
通過(guò)源碼最后可以得出
//HeapMataData的結(jié)構(gòu)如下
struct HeapMataData {
var kind: Int
var superClass: Any.Type
var cacheData: (Int, Int)
var data: Int
var classFlags: Int32
var instanceAddressPoint: UInt32
var instanceSize: UInt32
var instanceAlignmentMask: UInt16
var reserved: UInt16
var classSize: UInt32
var classAddressPoint: UInt32
var typeDescriptor: UnsafeMutableRawPointer
var iVarDestroyer: UnsafeRawPointer
}