1.延遲存儲屬性
class Teacher {
lazy var age:Int = 10
}
1.用lazy修飾的存儲屬性
2.延遲存儲屬性必須有一個默認(rèn)的初始值
我們不給age賦默認(rèn)值會報錯,惰性屬性必須具有初始化程序,如果我們修改為可選 lazy var age:Int?,還是報同樣的錯誤
3.延遲存儲屬性第一次訪問時才會被賦值
let t = Teacher()
t.age = 30
print("end")
我們在第一次訪問之前和之后訪問t的內(nèi)存情況

我們看一下,加lazy和不加lazy,Teacher的大小是不是相同
print(class_getInstanceSize(Teacher.self))
不加lazy的大小是24,加lazy的大小是32
為什么加了lazy之后會增加8個字節(jié)呢
看sil代碼
swiftc -emit-sil main.swift | xcrun swift-demangle >> ./main.sil

我們看到lazy修飾的屬性,是一個optional的變量
我們第一次getter方式時

我們第一次獲取屬性的值時,Optional的值時none,經(jīng)過bb2賦值后變成case是some值為10
Optional的值多大呢
我們可以通過下面的命令打印看出
print(MemoryLayout<Optional<Int>>.size)
print(MemoryLayout<Optional<Int>>.stride)
size為9,stride為16,Optional<Int>的尺寸大小,在后面會有詳細(xì)介紹。stride為16是因為字節(jié)對齊。
4.延遲存儲屬性并不能保證線程安全

這就導(dǎo)致age被初始化兩次
5.延遲存儲屬性對實例對象大小的影響
如果存儲屬性時Int,不是延遲存儲屬性,則占8字節(jié),如果是延遲存儲屬性則占9字節(jié),9因為不是8的倍數(shù),可能會出現(xiàn)內(nèi)存對齊分配的內(nèi)存可能會更大。
2.類型屬性
class Teacher {
static var age:Int = 10
}
1.用static修飾的存儲屬性
2.類型屬性必須有一個默認(rèn)值
如果不給默認(rèn)值

3.類型屬性只會被初始化一次
我們還是通過sil看

類型屬性是用單利屬性初始化的
3.單利的正確寫法
1.OC的單利寫法
static OCClass*_ocObject = nil;
+ (instancetype)shareManager {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_ocObject = [[OCClass alloc] init];
});
return _ocObject;
}
2.swift的單利寫法
class Teacher {
static let sharedInstance:Teacher = Teacher()
private init() {}
}
使用static let創(chuàng)建聲明一個實例對象
給當(dāng)前init添加訪問控制權(quán)限private
4.結(jié)構(gòu)體的初始化
1.結(jié)構(gòu)體不需要自定義初始化方法
struct Teacher {
var age:Int
var name:String
}
class Student {
var age:Int
var name:String
}
在class中會報 Class 'Student' has no initializers,這是因為編譯器在結(jié)構(gòu)體中自動幫助我們合成初始化方法,也就意味我們可以直接這樣調(diào)用
var teach = Teacher(age: 12, name: "Hello")
查看sil代碼
struct Teacher {
@_hasStorage var age: Int { get set }
@_hasStorage var name: String { get set }
init(age: Int, name: String)
}
2.如果我們的屬性有初始化值,系統(tǒng)會提供不同的默認(rèn)初始化方法
struct Teacher {
var age:Int = 18
var name:String = "Hello"
}

3.如果我們自定義初始化方法,系統(tǒng)就不會幫我們生成初始化方法
struct Teacher {
var age:Int = 18
var name:String = "Hello"
init(age:Int,name:String) {
self.age = age;
self.name = name
}
}

5.結(jié)構(gòu)體是值類型
1.什么是類型
func test() {
var age = 18
var age2 = age
age = 30
age2 = 45
print("age \(age) age2 \(age2)")
}
在age = 30打斷點和print處打斷點查看內(nèi)存情況

直接修改地址內(nèi)的值18(0x12)-->30(0x1e) 18(0x12)--->45(0x2d)
用withUnsafeMutablePointer(to: &age){print(&0)}查看指針地址
1.值類型,地址中存儲的是值
2.值類型傳遞的過程是傳遞的副本
6.mutating和inout
mutating
如果我們定義一個stack,類型為struct,
struct MyStack {
var items = [Int]()
func push(_ item:Int) {
items.append(item)
}
}
報錯 Cannot use mutating member on immutable value: 'self' is immutable
我們暫且修改代碼
struct MyStack {
var items = [Int]()
func push(_ item:Int) {
print("end")
}
}
查看sil

在push中我們只所以能夠訪問 items,是因為push內(nèi)有一個默認(rèn)的參數(shù)self,但是我們現(xiàn)在看到self是let,因為MyStack是值類型,let修飾后就不能改變MyStack的值了,所以在上面的items.append(item)就會報錯,我們怎么解決這個問題呢?
用mutating修飾
struct MyStack {
var items = [Int]()
mutating func push(_ item:Int) {
items.append(item)
}
}
查看sil經(jīng)過mutabting修飾之后默認(rèn)參數(shù)self就是var類型的

mutabting的實質(zhì)是讓函數(shù)的參數(shù)增加inout修飾
2. inout
func swapTwoNumber(a:Int,b: Int) {
let temp = a
a = b
b = temp
}
這種寫法報錯 Cannot assign to value: 'a' is a 'let' constant
函數(shù)中參數(shù)默認(rèn)是let類型,我們無法進(jìn)行修改

如果我們想修改默認(rèn)傳入的參數(shù),我們使用inout進(jìn)行修飾
func swapTwoNumber(a:inout Int,b: Int) {
}
我們看sil,看inout做了什么

經(jīng)過inout修飾的是地址取值,并用var修飾。而沒用inout修飾的,簡單的復(fù)制并且是let類型
7.結(jié)構(gòu)體方法調(diào)用
結(jié)構(gòu)體中的方法調(diào)度是靜態(tài)調(diào)度(編譯,鏈接完成之后當(dāng)前的函數(shù)地址就已經(jīng)確定存放在了代碼段)
1.查看當(dāng)前mach文件的符號表
nm mach文件路徑

2.還原符號
xcrun swift-demangle s13SwiftProperty9MyTeacherV7teacheryyF
