Swift進(jìn)階-類與結(jié)構(gòu)體
Swift-函數(shù)派發(fā)
Swift進(jìn)階-屬性
Swift進(jìn)階-指針
Swift進(jìn)階-內(nèi)存管理
Swift進(jìn)階-TargetClassMetadata和TargetStructMetadata數(shù)據(jù)結(jié)構(gòu)源碼分析
Swift進(jìn)階-Mirror解析
Swift進(jìn)階-閉包
Swift進(jìn)階-協(xié)議
Swift進(jìn)階-泛型
Swift進(jìn)階-String源碼解析
Swift進(jìn)階-Array源碼解析
在類與結(jié)構(gòu)體章節(jié)講到:通過匯編調(diào)試和swift源碼知道我們的 純swift類的對象 Teacher() 的內(nèi)存分配: __allocating_init -> _swift_allocObject_ -> swift_slowAlloc -> malloc

其中_swift_allocObject_里面創(chuàng)建一個(gè) HeapObject對象并將其返回了,所以這個(gè)HeapObject就是我們實(shí)際創(chuàng)建的對象,來看看其初始化函數(shù):
constexpr HeapObject(HeapMetadata const *newMetadata)
: metadata(newMetadata)
, refCounts(InlineRefCounts::Initialized)
{ }
了解上面內(nèi)容后,來看看創(chuàng)建一個(gè)對象,輸出它的地址,格式化輸出它的內(nèi)容:
class Teacher {
var name: String
init(name: String) {
self.name = name
}
}
class ViewController: UIViewController{
override func viewDidLoad() {
super.viewDidLoad()
let teacher = Teacher(name: "陳老師")
// 通過Unmanaged指定內(nèi)存管理,類似于OC與CF的交互方式(所有權(quán)的轉(zhuǎn)換 __bridge)
// passUnretained 不增加引用計(jì)數(shù),即不需要獲取所有權(quán)
// passRetained 增加引用技術(shù),即需要獲取所有權(quán)
// toOpaque 不透明的指針
let ptr = Unmanaged.passUnretained(teacher as AnyObject).toOpaque()
print(ptr)
print("end") // 斷點(diǎn)在這里
}
}

通過控制臺輸出后得到上圖,而x/8g輸出的前16個(gè)字節(jié)就是matadata和refcounts,這里的refcounts就是本章節(jié)研究的內(nèi)容。
ps: 注意此時(shí)要用Unmanaged.passUnretained去輸出teacher的指針,而不能在控制臺上 po teacher!因?yàn)?po teacher 會對其進(jìn)行引用,就不是上面這個(gè)結(jié)果了,來看看:

本章節(jié)圍繞一個(gè)問題探究引用計(jì)數(shù):那為什么一次引用后會變成這個(gè)數(shù)值0x0000000200000002?

1.引用計(jì)數(shù)的實(shí)質(zhì)
從源碼中HeapObject.h的找到refcounts的定義



RefCounts其實(shí)就是對我們當(dāng)前引用計(jì)數(shù)的包裝類,而引用計(jì)數(shù)的具體類型取決于傳遞給RefCounts的參數(shù) -> InlineRefCountBits。
找到InlineRefCountBits的定義:
typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;
它同樣是一個(gè)模板類RefCountBitsT

RefCountIsInline傳遞的是true/false,再去看看模板類RefCountBitsT:

RefCountBitsT就是我們真實(shí)操作引用計(jì)數(shù)的類,它只有一個(gè)屬性 bits,它的類型定義是 RefCountBitsInt

引用計(jì)數(shù)的實(shí)質(zhì): bits其實(shí)就是一個(gè)uint64位的位域信息,它存儲了就是運(yùn)行周期相關(guān)的引用計(jì)數(shù)(Swift和OC都是一樣的64位的位域信息)。
1.1 提問:當(dāng)創(chuàng)建一個(gè)引用對象的時(shí)候,它的引用計(jì)數(shù)是多少?
來看看源碼中的創(chuàng)建對象函數(shù)_swift_allocObject_


引用計(jì)數(shù)的初始化賦值,傳遞了一個(gè)Initialized,點(diǎn)進(jìn)去看看Initialized定義,在RefCount.h找到它是一個(gè)枚舉:

可以看到源碼的注釋:新對象的Refcount為1。
RefCountBits就是模板類RefCountBitsT(它有一個(gè)bits屬性)
找到RefCountBitsT初始化函數(shù),很快能定位到傳遞的參數(shù)strongExtraCount: 0和unownedCount: 1是怎么操作bits的:

0左移33位還是0,1左移1位是2,所以refCounts是0x2。
當(dāng)聲明一個(gè)引用對象,該對象沒有被引用的時(shí)候,refCounts是0x0000000000000002:
let teacher = Teacher(name: "陳老師")
let ptr = Unmanaged.passUnretained(teacher as AnyObject).toOpaque()
print(ptr)
print("end")


當(dāng)teacher被引用的時(shí)候,其refCounts是0x0000000200000002:
let teacher = Teacher(name: "陳老師")
let ptr = Unmanaged.passUnretained(teacher as AnyObject).toOpaque()
print(ptr)
print("end")
let t = teacher
print("end")

1左移33位,1左移1位是2,所以refCounts是0x0000000200000002。

如果再被引用一次,其refCounts是0x0000000400000002
2左移33位,1左移1位是2,所以refCounts是0x0000000400000002。

結(jié)論:強(qiáng)引用計(jì)數(shù)和無主引用計(jì)數(shù)是通過位移的方式,存儲在這64位的信息當(dāng)中。

1.2 提問:強(qiáng)引用是如何添加的呢?
從swift源碼中找到HeapObject.cpp找到_swift_retain_函數(shù)實(shí)現(xiàn):


引用計(jì)數(shù)+1:

引用計(jì)數(shù)-1:

2.循環(huán)引用問題
經(jīng)典案例:
class WJTeacher {
var age: Int = 18
var name: String = "林老師"
var subject: WJSubject?
}
class WJSubject {
var subjectName: String
var subjectTeacher: WJTeacher
init(_ subjectName: String, _ subjectTeacher: WJTeacher) {
self.subjectName = subjectName
self.subjectTeacher = subjectTeacher
}
}
class ViewController: UIViewController{
override func viewDidLoad() {
super.viewDidLoad()
let teacher = WJTeacher()
let subject = WJSubject.init("Swift進(jìn)階", teacher)
teacher.subject = subject
}
}
上面這段代碼兩個(gè)對象相互引用導(dǎo)致無法釋放。
Swift 提供了兩種辦法?來解決你在使 ?類的屬性時(shí)所遇到的循環(huán)強(qiáng)引?問題:弱引?( weak reference )和?主引?( unowned reference )。
weak弱引用解決上面的問題:
class WJTeacher {
var age: Int = 18
var name: String = "Steven"
weak var subject: WJSubject? // 弱引用subject
}
class WJSubject {
var subjectName: String
var subjectTeacher: WJTeacher
init(_ subjectName: String, _ subjectTeacher: WJTeacher) {
self.subjectName = subjectName
self.subjectTeacher = subjectTeacher
}
}
或者使用unowned無主引用解決上面的問題
class WJTeacher {
var age: Int = 18
var name: String = "Steven"
var subject: WJSubject?
}
class WJSubject {
var subjectName: String
unowned var subjectTeacher: WJTeacher // 無主引用subjectTeacher
init(_ subjectName: String, _ subjectTeacher: WJTeacher) {
self.subjectName = subjectName
self.subjectTeacher = subjectTeacher
}
}
弱引用和無主引用有什么區(qū)別呢?
2.1 weak弱引用
Alaways Show Disassembly打開編調(diào)試,看看weak修飾對象在創(chuàng)建時(shí)調(diào)用流程
weak var t = WJTeacher()
print(t) // 斷點(diǎn)打在這里

在類與結(jié)構(gòu)體章節(jié)看過__allocating_init()的調(diào)用過程,沒有看到相關(guān)弱引用的內(nèi)容;接下來又調(diào)用了swift_weakInit函數(shù);找到swift源碼:



來看看allocateSideTable做了啥事兒

來找到side: HeapObjectSideTableEntry到底是啥玩意兒,全局搜索了一下找到了源碼中有這么一段注釋,直接給出了結(jié)論:

在swift里面存在著兩種引用計(jì)數(shù),一種是強(qiáng)引用包含的是strong RC + unowned RC + flags;一種是弱引用包含的是strong RC + unowned RC + weak RC + flags
來一下HeapObjectSideTableEntry的源碼:



可以發(fā)現(xiàn)弱引用與強(qiáng)引用共用一個(gè)模板類RefCounts 其實(shí)就是它:RefCountBitsT。
來看看引用計(jì)數(shù)操作模板類RefCountBitsT通過弱引用的方式創(chuàng)建的源碼:

weak方式創(chuàng)建RefCountBitsT,首先是將散列表右移3位,然后將62位和63位標(biāo)記為1。
通過逆反操作驗(yàn)證上面源碼分析的的準(zhǔn)確性:

weak引用后refCounts那8位返回的是一個(gè)內(nèi)存地址(而不是引用計(jì)數(shù)64位域)

1.我們要還原,先將62位63位的標(biāo)志位設(shè)置為0得到的結(jié)果:

2.還要將這個(gè)結(jié)果向左移動3位:

將這個(gè)結(jié)果在控制臺上格式化輸出:

weak引用一個(gè)對象本質(zhì)上就是創(chuàng)建一個(gè)散列表
2.2 unowned與weak
上面介紹了強(qiáng)引用計(jì)數(shù)和無主引用計(jì)數(shù)是通過位移的方式,存儲在這64位的信息當(dāng)中的。
-
unowned不允許被引用的對象有nil的可能,無需要新建/維護(hù)散列表,直接可以通過64位的位域操作即可進(jìn)行引用計(jì)數(shù)。 -
weak它所引用的對象允許為nil,它需要新建/維護(hù)散列表
共同點(diǎn):
- 引用對象的自動引用計(jì)數(shù)都不會加1,不會造成對引用對象的強(qiáng)引用。
解決block引用計(jì)數(shù)問題:
class WJTeacher {
var age: Int = 18
var name: String = "Steven"
var closure: (()->())?
deinit {
print("WJTeacher 消失")
}
}
func test() {
let t = WJTeacher()
var closure = {
t.age = 19
}
t.closure = closure
closure()
}
此時(shí)循環(huán)引用:t->closure->t
修改后:
class WJTeacher {
var age: Int = 18
var name: String = "Steven"
var closure: (()->())?
deinit {
print("WJTeacher 消失")
}
}
func test() {
let t = WJTeacher()
var closure = { [weak t] in
t?.age = 19
}
t.closure = closure
closure()
}
// 或者
/**
func test() {
let t = WJTeacher()
var closure = { [unowned t] in
t.age = 19
}
t.closure = closure
closure()
}
*/