Swift指針&內(nèi)存管理

一、指針

??

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ò)storeload訪問(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ò)案例
    報(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ù)是多少艺智。

案例結(jié)果

查看源碼看看HeapObject結(jié)構(gòu)體中引用計(jì)數(shù)Refcount是如何定義的:

按住command + 單擊可查看其定義和引用的地方拱燃,可發(fā)現(xiàn)其是一個(gè)模板類(lèi)typedef RefCounts<InlineRefCountBits> InlineRefCounts;其是RefCounts類(lèi),接收的是InlineRefCountBits

InlineRefCounts定義
RefCounts類(lèi)
RefCountsBitsT類(lèi)

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ǔ)在散列表中。

image.png

再?gòu)囊粋€(gè)對(duì)象初始化分配內(nèi)存的時(shí)候引用計(jì)數(shù)到底做了啥弄跌?


_swift_allocObject_
image.png
RefCounts初始化化

可找到初始化一個(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)的初始化方法:
RefCountBitsT初始化方法

其中的StrongExtraRefCountShift铛只、PureSwiftDeallocShiftUnownedRefCountShift的值如下分析:

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方法:

匯編調(diào)用swift_retain

然后找到源碼嘉抒,實(shí)際是調(diào)用對(duì)象的屬性refCountsincrement方法:object->refCounts.increment(1);

源碼swift_retain
// 引用計(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
1<<33

所以上面示例打印內(nèi)存得到的引用計(jì)數(shù)的值就可以得到驗(yàn)證了

image.png

??然而強(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")
調(diào)用swift_weakInit

全局搜索源碼:


swift_weakInit方法
WeakReference類(lèi)

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;
}
image.png

同樣像強(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)有值揭糕。


image.png
image.png

弱引用與無(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í)候散列表生成:

    1. 弱引用被形成矾踱,以及有pending future implementation
    2. 強(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)有了

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末溪掀,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子召调,更是在濱河造成了極大的恐慌膨桥,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件唠叛,死亡現(xiàn)場(chǎng)離奇詭異只嚣,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)艺沼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)册舞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人障般,你說(shuō)我怎么就攤上這事调鲸。” “怎么了挽荡?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵藐石,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我定拟,道長(zhǎng)于微,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任青自,我火速辦了婚禮株依,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘延窜。我一直安慰自己恋腕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布逆瑞。 她就那樣靜靜地躺著荠藤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪获高。 梳的紋絲不亂的頭發(fā)上哈肖,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音谋减,去河邊找鬼。 笑死扫沼,一個(gè)胖子當(dāng)著我的面吹牛出爹,可吹牛的內(nèi)容都是我干的庄吼。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼严就,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼总寻!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起梢为,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤渐行,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后铸董,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體祟印,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年粟害,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蕴忆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡悲幅,死狀恐怖套鹅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情汰具,我是刑警寧澤卓鹿,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站留荔,受9級(jí)特大地震影響吟孙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜存谎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一拔疚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧既荚,春花似錦稚失、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至晴叨,卻和暖如春凿宾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背兼蕊。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工初厚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓产禾,卻偏偏與公主長(zhǎng)得像排作,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子亚情,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容