一、指針
??
1、指針類(lèi)型
??Swift中的指針?lè)譃閮深?lèi):指定數(shù)據(jù)類(lèi)型的指針(typed pointer
);未指定數(shù)據(jù)類(lèi)型的指針麸拄,也叫原生指針(raw pointer
),基本上有以下:
Swift | OC | 備注 |
---|---|---|
unsafePointer<T> | const T * | 指針及所指向的內(nèi)容都不可變 |
unsafeMutablePointer<T> | T * | 指針及所指向的內(nèi)存內(nèi)容均可變 |
unsafeRawPointer | const void * | 指針指向的內(nèi)存區(qū)域未定 |
unsafeMutableRawPointer | void * | 指針指向的內(nèi)存區(qū)域未定 |
unsafeBufferPointer<T> | 指針指向指定類(lèi)型的連續(xù)緩沖區(qū)不可變 | |
unsafeMutableBufferPointer<T> | 指針指向指定類(lèi)型的連續(xù)緩沖區(qū)可變 | |
unsafeRawBufferPointer | 指針指向未定類(lèi)型的連續(xù)緩沖區(qū)不可變 | |
unsafeMutableRawBufferPointer | 指針指向未定類(lèi)型的連續(xù)緩沖區(qū)可變 |
2星著、原始指針的使用
我們先了解一下MemoryLayout <T>
指定類(lèi)型的內(nèi)存布局這個(gè)枚舉類(lèi)型。
public enum MemoryLayout<T> {
?? public static var size: Int { get }
?? public static var stride: Int { get }
?? public static var alignment: Int { get }
?? public static func size(ofValue value: T) -> Int
?? public static func stride(ofValue value: T) -> Int
?? public static func alignment(ofValue value: T) -> Int
?? public static func offset(of key: PartialKeyPath<T>) -> Int?
}
成員/方法 | 例子 | 說(shuō)明 |
---|---|---|
var size: Int | MemoryLayout <Int>.size | 大小 |
var stride: Int | MemoryLayout <Int>.stride | 存儲(chǔ)從一個(gè)實(shí)例到下一個(gè)實(shí)例所需要的字節(jié)數(shù)量,也叫步長(zhǎng). |
var alignment: Int | MemoryLayout <Int>. alignment | 指定類(lèi)型的默認(rèn)內(nèi)存對(duì)齊 |
func size(ofValue value: T) -> Int | MemoryLayout.size(ofValue: "10.0") | 根據(jù)值計(jì)算內(nèi)存大小 |
func stride(ofValue value: T) -> Int | MemoryLayout.stride(ofValue: PSYModel()) | 根據(jù)值計(jì)算步長(zhǎng) |
func alignment(ofValue value: T) -> Int | MemoryLayout. alignment(ofValue: PSYModel()) | 根據(jù)值計(jì)算內(nèi)存對(duì)齊字節(jié) |
func offset(of key: PartialKeyPath<T>) -> Int? | -- |
struct PSYModel {
var age: Int = 18
var name: String = "psy"
var femalSex: Bool = false
}
print(MemoryLayout<String>.size) // String類(lèi)型大小
print(MemoryLayout<PSYModel>.size) // 當(dāng)前結(jié)構(gòu)體類(lèi)型的具體大小
print(MemoryLayout<PSYModel>.stride) // 當(dāng)前結(jié)構(gòu)體實(shí)例的步長(zhǎng)信息
print(MemoryLayout<PSYModel>.alignment) // 結(jié)構(gòu)體內(nèi)存對(duì)齊
輸出:
16
25
32
8
eg:
使用Raw Pointer來(lái)存儲(chǔ)4個(gè)整形的數(shù)據(jù)耘柱。
// 首先申請(qǐng)4個(gè)整形數(shù)據(jù)大小的內(nèi)存
let p = UnsafeMutableRawPointer.allocate(byteCount: MemoryLayout<Int>.size * 4, alignment: MemoryLayout<Int>. alignment)
// 遍歷循環(huán)并根據(jù)偏移存入數(shù)據(jù)
for i in 0..<4{
p.storeBytes(of: i, toByteOffset: (MemoryLayout<Int>.stride * i), as: Int.self)
//或者// p.advanced(by: i * MemoryLayout<Int>.stride).storeBytes(of: i, as: Int.self)
//或者// (p + i * 8).storeBytes(of: i, as: Int.self)
}
print(p)
// 循環(huán)根據(jù)偏移p.load讀取內(nèi)存值
for i in 0..<4{
let value = p.load(fromByteOffset: i*8, as: Int.self)
print("index:\(i), value:\(value)")
}
// 釋放內(nèi)存
p.deallocate()
3如捅、泛型指針的使用
??泛型指針相對(duì)于原生指針,其是指定了當(dāng)前綁定了具體的類(lèi)型帆谍。在訪問(wèn)泛型指針時(shí)并不是像原生指針那樣通過(guò)store
和load
訪問(wèn)內(nèi)存伪朽,而是通過(guò)泛型指針內(nèi)置屬性pointee
訪問(wèn)。
獲取到UnsafePointer的方式有兩種:
第一種: 通過(guò)已知變量獲取
使用withUnsafePointer
訪問(wèn)變量?jī)?nèi)存地址
var age = 3
withUnsafePointer(to: &age){ptr in
?? print("地址:(ptr) 值:(ptr.pointee)")
}
??
age = withUnsafePointer(to: &age){ptr in
?? return ptr.pointee + 21
}
print("age = (age)") // age = 24
要想在withUnsafePointer
的尾隨閉包中修改age的值汛蝙,就要使用withUnsafeMutablePointer
烈涮,如下:
withUnsafeMutablePointer(to: &age){ ptr in
?? ptr.pointee += 21
}
??
print(age) // 24
第二種: 直接分配內(nèi)存
var age = 10
// 分配一塊Int類(lèi)型的內(nèi)存空間,此時(shí)該內(nèi)存空間并沒(méi)有被初始化
let tPtr = UnsafeMutablePointer<Int>.allocate(capacity: 1)
// 分配的內(nèi)存空間初始化
tPtr.initialize(to: age)
// 通過(guò)pointee的屬性訪問(wèn)內(nèi)存的值
print(tPtr.pointee)
tPtr.deallocate() // 釋放
或
struct PSYModel{
? var age: Int
? let name: String
? var height: Double
}
// 為結(jié)構(gòu)體類(lèi)型的指定數(shù)量的實(shí)例分配未初始化的內(nèi)存窖剑。
var strPtr = UnsafeMutablePointer<PSYModel>.allocate(capacity: 5)
// 內(nèi)存初始化存儲(chǔ)
strPtr[0] = PSYModel(age: 18, name: "PSY", height: 185)
strPtr[1] = PSYModel(age: 17, name: "俏~", height: 170)
strPtr[2] = PSYModel(age: 11, name: "LH", height: 163)
// 將內(nèi)存空間清空(一般與deallocate成對(duì)出現(xiàn))
strPtr.deinitialize(count: 3)
// 釋放
strPtr.deallocate()
內(nèi)存初始化存儲(chǔ)還可以將strPtr[0] = PSYModel(age: 18, name: "PSY", height: 185)
改成:
(strPtr + 0).initialize(to: PSYModel(age: 18, name: "PSY", height: 185.0))
或者:
strPtr.advanced(by: MemoryLayout<PSYModel>.stride * 0).initialize(to: PSYModel(age: 18, name: "PSY", height: 185))
以上這三中初始化方式是等價(jià)的坚洽。
deinitialize(count: 3)
是要根據(jù)上面初始化的數(shù)量,進(jìn)行清空內(nèi)存西土,初始化了 3個(gè) 讶舰,count
就傳 3
4、內(nèi)存指針的使用
將一個(gè)對(duì)象轉(zhuǎn)換成結(jié)構(gòu)體指針需了,期間因?yàn)樾枰獙?duì)指針操作跳昼,所以需要管理內(nèi)存,既繁瑣有不安全肋乍,所以使用Unmanaged(非托管)鹅颊,通過(guò)passUnretained(_ value: Instance). toOpaque()
轉(zhuǎn)成指針,并不對(duì)引用計(jì)數(shù)執(zhí)行+1操作墓造,所以不用管理其內(nèi)存堪伍。相對(duì)應(yīng)的還有一個(gè)passRetained(_ value: Instance). toOpaque()
,其會(huì)對(duì)實(shí)例對(duì)象的引用計(jì)數(shù)執(zhí)行+1操作,所以使用需注意觅闽。
struct HeapObject{ // 堆內(nèi)存中的結(jié)構(gòu)體
??var metadata: UnsafeRawPointer // metadata原生指針
??var refcount1: Int32 // Int64帝雇, 可拆成兩個(gè)Int32位
??var refcount: Int32
}
??
struct MetaData{ // metadata的結(jié)構(gòu)體
?? var metadata: UnsafeRawPointer
?? var supperclass: UnsafeRawPointer
?? var cacheData1: UnsafeRawPointer
?? var cacheData2: UnsafeRawPointer
?? var data: UnsafeRawPointer
?? var flags: UInt32
?? var instanceAddressOffset: UInt32
?? var instanceSize: UInt32
?? var instanceAlignMask: UInt16
?? var reserved: UInt16
?? var classSize: UInt32
?? var classAddressOffset: UInt32
?? var description: UnsafeRawPointer
}
??
class PSYModel{
??var age: Int = 18
?? let name: String = "PSY"
}
??
var psy = PSYModel()
??
// 拿到實(shí)例的指針,但是對(duì)實(shí)例的引用計(jì)數(shù)沒(méi)有影響(Unmanaged非托管的蛉拙,將實(shí)例對(duì)象的轉(zhuǎn)成指針尸闸,但是對(duì)引用計(jì)數(shù)不執(zhí)行+1操作)
let psyRawPtr = Unmanaged.passUnretained(psy as AnyObject).toOpaque()
// 綁定成具體的結(jié)構(gòu)體類(lèi)型
let objPtr = psyRawPtr.bindMemory(to: HeapObject.self, capacity: 1)
??
let metadata = objPtr.pointee.metadata.bindMemory(to: MetaData.self, capacity: 1)
print("HeapObject:*********")
print(objPtr.pointee)
print("\n"); print("\n")
print("MetaData:*********")
print(metadata.pointee)
5、內(nèi)存綁定
Swift提供了三種不同的API來(lái)綁定/重綁定指針
-
assumingMemoryBound(to:)
??
使用場(chǎng)景:
在處理代碼時(shí)刘离,只有原是指針(UnsafeRawPointer)室叉,沒(méi)有保留指針類(lèi)型,但是我們明確知道指針的類(lèi)型硫惕,此時(shí)用assumingMemoryBound(to:)
綁定成指定類(lèi)型茧痕。此時(shí)編譯器不進(jìn)行此類(lèi)型匹配檢查,所以不需要類(lèi)型轉(zhuǎn)換恼除。
??
eg:報(bào)錯(cuò)案例
eg:正確用法
func testTuple(_ ptr: UnsafePointer<Int>){
print(ptr[0])
print(ptr[1])
}
let tuple = (12, 18)
withUnsafePointer(to: tuple){ (tuplePtr: UnsafePointer<(Int, Int)>) in
testTuple(UnsafeRawPointer(tuplePtr).assumingMemoryBound(to: Int.self))
}
UnsafeRawPointer(tuplePtr)
將tuplePtr轉(zhuǎn)成原生指針踪旷,然后調(diào)用assumingMemoryBound(to: T.Type)
綁定成指定類(lèi)型
bindMemory(to: capacity:)
??將內(nèi)存綁定到指定的類(lèi)型曼氛,并返回一個(gè)指向綁定內(nèi)存的類(lèi)型化指針。使用bindMemory(to:capacity:)
方法將這個(gè)指針引用的內(nèi)存綁定到類(lèi)型' T '上令野。內(nèi)存必須未初始化或初始化為布局與' T '兼容的類(lèi)型舀患。如果內(nèi)存未初始化,則綁定到' T '后仍未初始化气破。
eg:
let count = 4
let bytesPointer = UnsafeMutableRawPointer.allocate(byteCount: 100,alignment: MemoryLayout<Int8>.alignment)
let int8Pointer = bytesPointer.bindMemory(to: Int8.self, capacity: count)
withMemoryRebound(to: capacity: body:)
??執(zhí)行給定的閉包聊浅,同時(shí)將指定數(shù)量的實(shí)例臨時(shí)綁定到指定類(lèi)型。
當(dāng)您有一個(gè)內(nèi)存指針綁定到一種類(lèi)型现使,而您需要作為另一種類(lèi)型的實(shí)例訪問(wèn)該內(nèi)存時(shí)低匙,請(qǐng)使用此方法。以“T”類(lèi)型訪問(wèn)內(nèi)存需要將內(nèi)存綁定到該類(lèi)型碳锈。一個(gè)內(nèi)存位置一次只能綁定到一種類(lèi)型顽冶,因此訪問(wèn)與不相關(guān)的類(lèi)型相同的內(nèi)存而不首先重新綁定該內(nèi)存是沒(méi)有定義的。
從這個(gè)指針開(kāi)始的內(nèi)存區(qū)域售碳,包括指針的' Pointee '類(lèi)型的' count '實(shí)例必須被初始化强重。
// 一個(gè)指向指定類(lèi)型的已初始化指針
let uint64Pointer: UnsafePointer<UInt64> = 初始值
// 臨時(shí)重綁定到Int64類(lèi)型作為臨時(shí)使用
let isNegative = uint64Pointer.withMemoryRebound(to: Int64.self, capacity: 1) { ptr in
return ptr.pointee < 0
}
二、內(nèi)存管理
Swift中使用自動(dòng)引用計(jì)數(shù)機(jī)制(ARC)來(lái)管理內(nèi)存贸人。那就離不開(kāi)引用計(jì)數(shù)refCounts
间景,用代碼創(chuàng)建一個(gè)實(shí)例,看一下其內(nèi)存引用計(jì)數(shù)是多少艺智。
查看源碼看看HeapObject
結(jié)構(gòu)體中引用計(jì)數(shù)Refcount是如何定義的:
按住command + 單擊
可查看其定義和引用的地方拱燃,可發(fā)現(xiàn)其是一個(gè)模板類(lèi)typedef RefCounts<InlineRefCountBits> InlineRefCounts;
其是RefCounts
類(lèi),接收的是InlineRefCountBits
而RefCounts
類(lèi)接收的InlineRefCountBits
力惯,其定義是typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits
,是RefCountBitsT
基于這個(gè)類(lèi)接收RefCountIsInline
,而找到RefCountIsInline
其實(shí)是一個(gè)枚舉召嘶,如果RefCountIsInline = true
,則引用計(jì)數(shù)采用一定的編碼算法存儲(chǔ)在對(duì)象中父晶;如果RefCountNotInline = false
,則引用計(jì)數(shù)存儲(chǔ)在散列表中。
再?gòu)囊粋€(gè)對(duì)象初始化分配內(nèi)存的時(shí)候引用計(jì)數(shù)到底做了啥弄跌?
可找到初始化一個(gè)對(duì)象的引用數(shù)是1:constexpr RefCounts(Initialized_t) : refCounts(RefCountBits(0, 1)) {}
甲喝,其接收的是RefCountBits
這個(gè)類(lèi)型,正是該方法RefCounts
類(lèi)接收的類(lèi)型:
所以此時(shí)找到
RefCountBitsT
類(lèi)的初始化方法:其中的StrongExtraRefCountShift
铛只、PureSwiftDeallocShift
和UnownedRefCountShift
的值如下分析:
static const size_t PureSwiftDeallocShift = 0;
static const size_t PureSwiftDeallocBitCount = 1;
static const size_t UnownedRefCountShift = shiftAfterField(PureSwiftDealloc);
static const size_t UnownedRefCountBitCount = 31;
static const size_t IsDeinitingShift = shiftAfterField(UnownedRefCount);
static const size_t IsDeinitingBitCount = 1;
# define maskForField(name) (((uint64_t(1)<<name##BitCount)-1) << name##Shift)
# define shiftAfterField(name) (name##Shift + name##BitCount)
static const size_t StrongExtraRefCountShift = shiftAfterField(IsDeiniting);
static const size_t StrongExtraRefCountBitCount = 30;
StrongExtraRefCountShift = shiftAfterField(IsDeiniting)
= IsDeinitingShift + IsDeinitingBitCount
= shiftAfterField(UnownedRefCount) + IsDeinitingBitCount
= UnownedRefCountShift + UnownedRefCountBitCount + IsDeinitingBitCount
= PureSwiftDeallocShift + PureSwiftDeallocBitCount + UnownedRefCountBitCount + IsDeinitingBitCount
= 0 + 1 + 31 + 1
= 33
PureSwiftDeallocShift = 0
UnownedRefCountShift = 1
所以初始化方法變成了:
constexpr
RefCountBitsT(uint32_t strongExtraCount, uint32_t unownedCount)
: bits((BitsType(strongExtraCount) << 33 | (BitsType(1) << 0) | (BitsType(unownedCount) << 1)
{ }
再根據(jù)傳入的初始值埠胖,strongExtraCount = 0,unownedCount = 1
constexpr
RefCountBitsT(0, 1) : bits((0 << 33 | (1 << 0) | (1 << 1){ }
0 << 33 = 0
1 << 0 = 1
1 << 1 = 2
最終結(jié)果是:0 | 1 | 2 = 3
跟代碼運(yùn)行的結(jié)果 2 不匹配淳玩,個(gè)人認(rèn)為應(yīng)該是系統(tǒng)版本和Xcode版本不匹配導(dǎo)致的(筆者M(jìn)ac OS 10.14.6直撤,Xcode 11.3,而筆者Swift是最新的5.6)蜕着,找了以前的源碼谋竖,初始化方法中并沒(méi)有(BitsType(1) << 0)
,也就少了1红柱,最終的值也就是 2,真好和我當(dāng)前的Xcode測(cè)試結(jié)果一致蓖乘。
1锤悄、強(qiáng)引用
強(qiáng)引用到底是如何對(duì)Refcount操作呢,筆者通過(guò)匯編發(fā)現(xiàn)其實(shí)際調(diào)用的是swift_retain
方法:
然后找到源碼嘉抒,實(shí)際是調(diào)用對(duì)象的屬性refCounts的increment
方法:object->refCounts.increment(1);
// 引用計(jì)數(shù)加 1 的方法
SWIFT_ALWAYS_INLINE
void increment(uint32_t inc = 1) {
auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
// constant propagation will remove this in swift_retain, it should only
// be present in swift_retain_n
if (inc != 1 && oldbits.isImmortal(true)) {
return;
}
RefCountBits newbits;
do {
newbits = oldbits;
// inc = 1, 調(diào)用incrementStrongExtraRefCount(1)函數(shù)
bool fast = newbits.incrementStrongExtraRefCount(inc);
if (SWIFT_UNLIKELY(!fast)) {
if (oldbits.isImmortal(false))
return;
return incrementSlow(oldbits, inc);
}
} while (!refCounts.compare_exchange_weak(oldbits, newbits,
std::memory_order_relaxed));
}
||
||
\||/
V
// incrementStrongExtraRefCount(1)函數(shù)零聚,返回True,就表示只有inlineRefcount些侍,返回false:表示有溢出隶症,表明有散列表
SWIFT_NODISCARD SWIFT_ALWAYS_INLINE bool
incrementStrongExtraRefCount(uint32_t inc) {
// bits += 1<< 33
bits += BitsType(inc) << Offsets::StrongExtraRefCountShift;
return (SignedBitsType(bits) >= 0);
}
||
||
\||/
V
所以上面示例打印內(nèi)存得到的引用計(jì)數(shù)的值就可以得到驗(yàn)證了
??然而強(qiáng)引用會(huì)帶來(lái)的風(fēng)險(xiǎn)是,如果A強(qiáng)引用B娩梨,B強(qiáng)引用A(你中有我沿腰,我中有你),就造成了循環(huán)引用狈定,導(dǎo)致的直接后果是對(duì)象得不到釋放颂龙,導(dǎo)致內(nèi)存泄漏,最終可能導(dǎo)致內(nèi)存溢出纽什。
eg:典型的 “我中有你措嵌,你中有我” 循環(huán)引用案例
class PSYModel{
var age: Int = 19
let name: String = "PSY"
var girlFriend: QLRModel?
}
class QLRModel{
var age: Int = 18
let name: String = "LQR"
var boyFriend: PSYModel?
init(_ age: Int, _ husband: PSYModel) {
self.age = age
self.boyFriend = husband
}
}
let psy = PSYModel()
let lqr = QLRModel.init(18, psy)
print("end")
Swift中解決循環(huán)引用的兩種方式:使用弱引用(weak reference
)和無(wú)主引用(unowned reference
)。
2芦缰、弱引用
所以在聲明屬性或?qū)嵗兞繒r(shí)企巢,在前面加上weak
關(guān)鍵字表明其實(shí)一個(gè)弱引用。弱引用并不會(huì)強(qiáng)持有實(shí)例對(duì)象让蕾,在ARC中浪规,實(shí)例對(duì)象被釋放的時(shí)候,弱引用會(huì)被置空為nil(可選類(lèi)型)探孝,這樣就不會(huì)導(dǎo)致內(nèi)存泄漏笋婿。
class PSYModel{
var age: Int = 19
let name: String = "PSY"
}
weak var psy = PSYModel()
print("end")
全局搜索源碼:
swift_weakInit
里面調(diào)用了ref->nativeInit(value)
,是屬于WeakReference方法,
// nativeInit方法
void nativeInit(HeapObject *object) {
auto side = object ? object->refCounts.formWeakReference() : nullptr;
nativeValue.store(WeakReferenceBits(side), std::memory_order_relaxed);
}
||
|| // refCounts.formWeakReference
\||/
V
template <>
HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::formWeakReference()
{
auto side = allocateSideTable(true);
if (side)
return side->incrementWeak();
else
return nullptr;
}
||
|| // setNativeOrNull
\||/
V
// 如果有必要顿颅,創(chuàng)建并返回對(duì)象的散列表.
// 如果對(duì)象正在銷(xiāo)毀缸濒,返回空.
template <>
HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::allocateSideTable(bool failIfDeiniting)
{
auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
// 做一些必要的預(yù)處理
if (oldbits.hasSideTable()) { // 如果已存在散列表直接返回.
return oldbits.getSideTable();
}
else if (failIfDeiniting && oldbits.getIsDeiniting()) { // 如果對(duì)象正在銷(xiāo)毀,返回空.
return nullptr;
}
// FIXME: 創(chuàng)建散列表
HeapObjectSideTableEntry *side = new HeapObjectSideTableEntry(getHeapObject());
// 初始化
auto newbits = InlineRefCountBits(side);
do {
if (oldbits.hasSideTable()) {
// 如果舊的有散列表粱腻,返回它庇配,并刪除新創(chuàng)建的。
// Read before delete to streamline barriers.
auto result = oldbits.getSideTable();
delete side;
return result;
}
else if (failIfDeiniting && oldbits.getIsDeiniting()) {
// Already past the start of deinit. Do nothing.
return nullptr;
}
//
side->initRefCounts(oldbits);
} while (! refCounts.compare_exchange_weak(oldbits, newbits,
std::memory_order_release,
std::memory_order_relaxed));
return side;
}
同樣像強(qiáng)引用一樣找到RefCountBitsT以一個(gè)HeapObjectSideTableEntry初始化的方法:
SWIFT_ALWAYS_INLINE
RefCountBitsT(HeapObjectSideTableEntry* side)
: bits((reinterpret_cast<BitsType>(side) >> Offsets::SideTableUnusedLowBits)
| (BitsType(1) << Offsets::UseSlowRCShift)
| (BitsType(1) << Offsets::SideTableMarkShift))
{
assert(refcountIsInline);
}
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);
# define maskForField(name) (((uint64_t(1)<<name##BitCount)-1) << name##Shift)
# define shiftAfterField(name) (name##Shift + name##BitCount)
static const size_t StrongExtraRefCountShift = shiftAfterField(IsDeiniting);
static const size_t StrongExtraRefCountBitCount = 30;
UseSlowRCShift = StrongExtraRefCountShift + StrongExtraRefCountBitCount
= IsDeinitingShift + IsDeinitingBitCount + StrongExtraRefCountBitCount
= UnownedRefCountShift + UnownedRefCountBitCount + IsDeinitingBitCount + StrongExtraRefCountBitCount
= PureSwiftDeallocShift + PureSwiftDeallocBitCount + UnownedRefCountBitCount + IsDeinitingBitCount + StrongExtraRefCountBitCount
= 0 + 1 + 31 + 1 + 30
= 63
SideTableMarkShift = SideTableBitCount
= 62
SideTableUnusedLowBits = 3
所以初始化方法變成了:
RefCountBitsT(HeapObjectSideTableEntry* side)
: bits((reinterpret_cast<BitsType>(side) >> 3) | (BitsType(1) << 63) | (BitsType(1) << 62))
{
assert(refcountIsInline);
}
1 << 63 和1 << 62 相當(dāng)于最高位和次高位是1绍些,從[0~63]這64位最高前兩位是1
sild >> 3 表示第三位清空
OK捞慌,至此來(lái)個(gè)案例驗(yàn)證一下:
3、Unowned(無(wú)主引用)
類(lèi)似弱引用柬批,其不會(huì)牢牢持有實(shí)例卿闹,無(wú)主引用嘉定是永遠(yuǎn)有值揭糕。
弱引用與無(wú)主引用什么時(shí)候用呢?
如果兩個(gè)對(duì)象的生命周期完全和對(duì)方?jīng)]有關(guān)系锻霎,用weak
如果其中一個(gè)對(duì)象銷(xiāo)毀著角,另一個(gè)對(duì)象也跟著銷(xiāo)毀,可以用unowned
費(fèi)強(qiáng)引用對(duì)象擁有和強(qiáng)引用對(duì)象同樣的或者更長(zhǎng)的聲明周期旋恼,用unowned吏口。
總結(jié)
一個(gè)對(duì)象在概念上有三種引用。這些refcounts要么存儲(chǔ)在isa后面的字段中
inLine
冰更,要么存儲(chǔ)在isa后面的字段所指向的side table entry (散列表)
中产徊。強(qiáng)引用:
強(qiáng)RC計(jì)數(shù)對(duì)象的強(qiáng)引用(strong references
)。當(dāng)強(qiáng)RC達(dá)到零時(shí)蜀细,對(duì)象被銷(xiāo)毀舟铜,無(wú)主引用(unowned
)讀取將出現(xiàn)錯(cuò)誤,而弱引用(weak reference
)讀取將變成 nil 奠衔。強(qiáng)RC被存儲(chǔ)為一個(gè)額外的計(jì)數(shù):物理字段為0
谆刨,邏輯值為1
。無(wú)主引用:
無(wú)主RC計(jì)算對(duì)該對(duì)象的無(wú)主引用归斤。無(wú)主RC也有一個(gè)額外的+1代表強(qiáng)引用;這個(gè)+1在deinit
完成后減少痊夭。當(dāng)無(wú)主RC達(dá)到零時(shí),該對(duì)象的分配將被釋放脏里。弱引用:
弱RC計(jì)數(shù)對(duì)象的弱引用她我。弱RC也有一個(gè)額外的+1代表無(wú)主引用;這個(gè)+1在對(duì)象的分配被釋放后遞減。當(dāng)弱RC達(dá)到0時(shí)迫横,對(duì)象的散列表將被釋放番舆。
-
對(duì)象開(kāi)始時(shí)是沒(méi)有散列表的。當(dāng)以下情形的時(shí)候散列表生成:
- 弱引用被形成矾踱,以及有
pending future implementation
- 強(qiáng)引用溢出或無(wú)主引用溢出(內(nèi)聯(lián)引用在32位系統(tǒng)上是很小的)
3.一個(gè)對(duì)象需要關(guān)聯(lián)對(duì)象存儲(chǔ)時(shí)
獲取散列表是一種單向操作合蔽,具有散列表的對(duì)象永遠(yuǎn)不會(huì)丟失它。這可以防止一些線程競(jìng)爭(zhēng)介返。
- 弱引用被形成矾踱,以及有
強(qiáng)的和無(wú)主的變量指向這個(gè)對(duì)象。弱變量指向?qū)ο蟮纳⒘斜怼?/p>
存儲(chǔ)布局:
HeapObject {
?? isa
?? InlineRefCounts {
???? atomic<InlineRefCountBits> {
?????? strong RC + unowned RC + flags
?????? OR
?????? HeapObjectSideTableEntry*
???? }
?? }
}?
??
HeapObjectSideTableEntry {
?? SideTableRefCounts {
???? object pointer
???? atomic<SideTableRefCountBits> {
?????? strong RC + unowned RC + weak RC + flags
???? }
?? }
}
??
對(duì)象的生命周期狀態(tài):
LIVE without side table
對(duì)象的引用初始化為1個(gè)強(qiáng)引用沃斤,1個(gè)無(wú)主引用圣蝎,1個(gè)弱引用
沒(méi)有散列表,沒(méi)有弱引用存儲(chǔ)
強(qiáng)變量和無(wú)主變量正常工作
弱變量load不會(huì)發(fā)生
弱變量存儲(chǔ)添加了散列表衡瓶,成為LIVE with side table
當(dāng)強(qiáng)引用計(jì)數(shù)達(dá)到0時(shí)徘公,調(diào)用deinit()
同時(shí)對(duì)象成為正在DEINITING狀態(tài)
LIVE with side table
弱變量正常工作,其他的和LIVE一樣
DEINITING without side table
Deinit()正在處理該對(duì)象哮针。
強(qiáng)變量操作沒(méi)有效果关面。
在swift_abortretainun()
中坦袍,無(wú)主變量加載停止。
無(wú)主變量存儲(chǔ)正常工作等太。弱可變load不會(huì)發(fā)生捂齐。弱變量store存儲(chǔ)nil。
當(dāng)deinit()
完成時(shí)缩抡,生成的代碼調(diào)用swift_deallocObject
奠宜。而swift_deallocObject
調(diào)用canBeFreedNow()
快速檢查沒(méi)有弱的或無(wú)主的引用。如果canBeFreedNow()
對(duì)象被釋放并且它變成死的瞻想。否則压真,它會(huì)對(duì)無(wú)主RC進(jìn)行減量,使該對(duì)象變成DEINITED蘑险。
DEINITING with side table
弱變量加載返回nil滴肿。弱變量store存儲(chǔ)nil。
canBeFreedNow()
總是false佃迄,所以它永遠(yuǎn)不會(huì)直接過(guò)渡到DEAD泼差。
其他的都和DEINITING一樣。
DEINITED without side table
Deinit()
已經(jīng)完成和屎,但是還有一些無(wú)主引用拴驮。強(qiáng)變量操作不會(huì)發(fā)生。無(wú)主變量存儲(chǔ)不會(huì)發(fā)生柴信。
在swift_abortretainun()
中套啤,無(wú)主變量加載停止。弱變量操作不會(huì)發(fā)生随常。當(dāng)無(wú)主RC的值為零時(shí)潜沦,該對(duì)象將被釋放,并且它將成為DEAD狀態(tài)绪氛。
DEINITED with side table
弱變量加載返回nil唆鸡。弱變量存儲(chǔ)不會(huì)發(fā)生。
當(dāng)無(wú)主引用變?yōu)榱銜r(shí)枣察,該對(duì)象將被釋放争占,而弱引用將被釋放遞減,對(duì)象變成FREED序目。其他的都和DEINITED一樣臂痕。
FREED without side table
這個(gè)狀態(tài)不可能發(fā)生
FREED with side table
對(duì)象被釋放,但對(duì)散列表有弱引用未被釋放猿涨。
強(qiáng)變量操作不會(huì)發(fā)生握童。
無(wú)主的變量操作不會(huì)發(fā)生。
弱變量加載返回nil叛赚。
弱變量存儲(chǔ)不會(huì)發(fā)生澡绩。
當(dāng)弱引用達(dá)到零時(shí)稽揭,將釋放散列表,對(duì)象變成DEAD肥卡。
DEAD
對(duì)象和對(duì)象的散列表都沒(méi)有了