一.為什么說指針不安全
比如我們在創(chuàng)建一個對象的時候蘑斧,是需要在堆分配內(nèi)存空間的碰凶。但是這個內(nèi)存空間的聲明周期是有限的,也就意味著如果我們使用指針指向這塊內(nèi)容空間炕檩,如果當(dāng)前內(nèi)存空間的生命周期到了(引用計(jì)數(shù)為0)窖剑,那么我們當(dāng)前的指針是不是就變成了未定義的行為了坚洽,也就是成了
野指針
。我們創(chuàng)建的內(nèi)存空間是有邊界的西土,比如我們創(chuàng)建一個大小為10的數(shù)組讶舰,這個時候我們通過指針訪問到了
index = 11
的位置,這個時候是不是就越界了需了,訪問了一個未知的內(nèi)存空間跳昼。指針類型和內(nèi)存的值不一致,也是不安全的肋乍。(指針的值是一個內(nèi)存地址鹅颊,也就是指針指向數(shù)據(jù)的首地址,根據(jù)這個地址只能得到指針指向的開始位置墓造,并不知道是什么類型的堪伍,因此指針的類型決定了數(shù)據(jù)的類型。
比如此時的指針類型為Int8觅闽,而指向的數(shù)據(jù)為Int杠娱, 此時就會造成精度的缺失
)
二.typedPointer&rawPointer
Swift中的指針分為兩類,typed pointer
指定數(shù)據(jù)類型指針谱煤,raw pointer
未指定數(shù)據(jù)類型的指針(原生指針)摊求。
Swift | Object-C | 說明 |
---|---|---|
UnsafePointer<T> | const T * | 指針可變,所指向的內(nèi)容都不可變 |
UnsafeMutablePointer<T> | T * | 指針及所指向的內(nèi)容均可變 |
UnsafeRawPointer | const void * | 指針指向的內(nèi)存區(qū)域未定 |
UnsafeMutableRawPointer | void * | 指針指向的內(nèi)存區(qū)域未定 |
UnsafeBufferPointer<T> | ||
UnsafeMutableBufferPointer<T> | ||
UnsafeRawBufferPointer | ||
UnsafeMutableRawBufferPointer |
-
BufferPointer
表示申請連續(xù)的內(nèi)存空間
1.存儲4個整形的數(shù)據(jù)
使用UnsafeMutablePointer
//capacity:容量刘离, 4代表4個8字節(jié)(Int)
let p = UnsafeMutablePointer<Int>.allocate(capacity: 4)
for i in 0 ..< 4 {
(p + i).initialize(to: i)
//其他方式
// p[i] = i
// p.advanced(by: i).initialize(to: i)
}
for i in 0 ..< 4 {
print((p + i).pointee) // 0,1,2,3
//其他方式
// print(p[i])
// print(p.advanced(by: i).pointee)
}
defer {
//將內(nèi)存空間全部摸成0
p.deinitialize(count: 4)
//釋放分配的內(nèi)存空間
p.deallocate()
}
使用UnsafeMutableRawPointer
let p = UnsafeMutableRawPointer.allocate(byteCount: 32, alignment: 8)
for i in 0 ..< 4 {
p.advanced(by: i * MemoryLayout<Int>.stride).storeBytes(of: i, as: Int.self)
}
for i in 0 ..< 4 {
print(p.load(fromByteOffset: i * MemoryLayout<Int>.stride, as: Int.self)) // 0,1,2,3
}
defer {
//有allocate分配內(nèi)存室叉,就有deallocate將分配的內(nèi)存釋放
p.deallocate()
}
2.MemoryLayout
-
MemoryLayout<T>.size
表示T的實(shí)際大小 -
MemoryLayout<T>.stride
表示步長信息。也就是內(nèi)存對齊后的大小 -
MemoryLayout<T>.alignment
表示內(nèi)存對齊大小
struct LGTeacher {
var age = 18
var sex = true
}
print(MemoryLayout<LGTeacher>.size) //9
print(MemoryLayout<LGTeacher>.stride) //16
print(MemoryLayout<LGTeacher>.alignment) //8
3.泛型指針
- 泛型指針硫惕,其實(shí)就是
指定類型指針
修改age的值
var age = 10
//此時這里其實(shí)是將age = age + 10, 并不是改的指針指向的內(nèi)存
age = withUnsafePointer(to: age) {
$0.pointee + 10
}
print(age) //20
//修改指針指向的內(nèi)存
withUnsafeMutablePointer(to: &age) {
$0.pointee += 10
}
print(age) //30
4.withUnsafePointer和Unmanaged
獲取實(shí)例的指針使用Unmanaged
//獲取實(shí)例t的指針
Unmanaged.passUnretained(t).toOpaque()
獲取內(nèi)存地址withUnsafePointer
//探究withUnsafePointer中參數(shù)是否帶&對返回值的影響
//1.如果為值類型
var age = 10
//此時對值取地址茧痕,表示age的內(nèi)存地址
let agePtr = withUnsafePointer(to: &age){$0} // 0x0000000100008270。取內(nèi)存0x100008270: 0x000000000000000a 0x0000000100008270
//已經(jīng)將值從堆區(qū)拷貝到了棧區(qū)恼除,然后返回的值在當(dāng)前棧區(qū)的內(nèi)存地址踪旷。第二個8字節(jié)為值原有的內(nèi)存地址也就是agePtr(0x0000000100008270)
let ageStackPtr = withUnsafePointer(to: age){$0} // 0x00007ff7bfeff228。取內(nèi)存0x7ff7bfeff228: 0x000000000000000a 0x0000000100008270
//2.如果值為實(shí)例類型豁辉,理解了值類型令野,指針類型也很好理解
var t = LGTeacher()
//此時的t為指針,實(shí)則是取指針的內(nèi)存地址
let tPtr = withUnsafePointer(to: &t){$0} // 0x0000000100008258徽级。0x100008258: 0x000000010b56b300 0x00007ff7bfeff280
//此時的t為指針气破,按照值類型的邏輯,將t指針拷貝到棧區(qū)餐抢,然后第二個8字節(jié)存放的是t指針原有的內(nèi)存地址现使。返回的是t指針在棧區(qū)的內(nèi)存地址低匙。因此第二個8字節(jié)其實(shí)就是tPtr
let tStackPtr = withUnsafePointer(to: t){$0} // 0x00007ff7bfeff1f0。0x7ff7bfeff1f0: 0x000000010b56b300 0x0000000100008258
//此時的0x000000010b56b300就是指針t
三.使用指針讀取Mach-o中類名碳锈、屬性名稱及vtable
class LGTeacher {
var age = 18
var name = "Kody"
func teach() {
print("teach")
}
func teach1() {
print("teach1")
}
func teach2() {
print("teach2")
}
}
//表示__swift_types里的字節(jié)大小
var size: UInt = 0
//1.獲取__swift_types的內(nèi)存地址
var typesPtr = getsectdata("__TEXT", "__swift5_types", &size)
//print(typesPtr)
//2.獲取程序運(yùn)行的基地址
//獲取macho中header的地址顽冶,也就是程序的基地址
var machoHeaderPtr: UnsafePointer<mach_header>!
//在macOS Monterey版本下取3,在之前版本上取0就可以了
let count = _dyld_image_count()
for i in 0..<count {
if _dyld_get_image_header(i).pointee.filetype == MH_EXECUTE {
machoHeaderPtr = _dyld_get_image_header(i)
}
}
//print(machoHeaderPtr)
//3.通過__LINKEDIT拿到vmAddress(VM Address - File Offset) 或者通過__PAGEZERO拿到VM Size也就是VM Address
var vmAddress: UInt64 = 0
//方式1:通過__LINKEDIT
//var linkEditPtr = getsegbyname("__LINKEDIT")
//if let linkEdit = linkEditPtr?.pointee {
// vmAddress = linkEdit.vmaddr - linkEdit.fileoff
//}
//方式2:通過__PAGEZERO
var pageZeroPtr = getsegbyname("__PAGEZERO")
vmAddress = pageZeroPtr?.pointee.vmsize ?? 0
//4.獲取__swift5_types的offset
var typesOffset: UInt64 = 0
//bitPattern按相同的二進(jìn)制位售碳,將內(nèi)存地址轉(zhuǎn)化為Int類型再轉(zhuǎn)化為UInt64
typesOffset = UInt64(bitPattern: Int64(Int(bitPattern: typesPtr))) - vmAddress
//5.通過基地址獲取__swift5_types中Data LO中4字節(jié)偏移信息
//將首地址轉(zhuǎn)為UInt64
let machoHeaderPtr_Int = UInt64(bitPattern: Int64(Int(bitPattern: machoHeaderPtr)))
//拿到dataLo的地址
var dataLoAddress = machoHeaderPtr_Int + typesOffset
//將dataLo地址轉(zhuǎn)為指針
let dataLoPtr = withUnsafePointer(to: &dataLoAddress){$0}
//獲取dataLo的值, 4字節(jié)
let dataLoContent = UnsafePointer<UInt32>.init(bitPattern: UInt(dataLoAddress))?.pointee
//UnsafePointer<UInt32>.init(bitPattern: Int(exactly: dataLoAddress) ?? 0)?.pointee
//6.獲取TargetClassDescriptor的offSet
let tcdOffset = UInt64(dataLoContent!) + typesOffset - vmAddress
//7.獲取TargetClassDescriptor在內(nèi)存當(dāng)中的地址
let targetClassDescriptor_Int = machoHeaderPtr_Int + tcdOffset
struct TargetClassDescriptor{
/*
關(guān)于原碼渗稍、反碼、補(bǔ)碼
Int在計(jì)算機(jī)里存放的是補(bǔ)碼也就是反碼+1
比如一個Int8, 1和-1
1的原碼:00000001(存的是補(bǔ)碼)
1的反碼:11111110
1的補(bǔ)碼:11111111
1在內(nèi)存:00000001
-1的原碼:10000001
-1的反碼:01111110
-1的補(bǔ)碼:01111111
-1在內(nèi)存:01111111(存的是補(bǔ)碼)
*/
var flags: UInt32
var parent: UInt32
var name: UInt32
var accessFunctionPointer: UInt32
var fieldDescriptor: Int32
var superClassType: Int32
var metadataNegativeSizeInWords: UInt32
var metadataPositiveSizeInWords: UInt32
var numImmediateMembers: UInt32
var numFields: UInt32
var fieldOffsetVectorOffset: UInt32
var Offset: UInt32
var size: UInt32
//vTable
}
//8.將TargetClassDescriptor地址還原成TargetClassDescriptor指針
let targetclassDescriptorPtr = UnsafePointer<TargetClassDescriptor>.init(bitPattern: UInt(targetClassDescriptor_Int))
//9.解析類名稱
if let name = targetclassDescriptorPtr?.pointee.name {
//獲取nameOffset
let nameOffset = Int64(name) + Int64(tcdOffset) + 8 - Int64(vmAddress)
//name在程序中的真實(shí)地址
let nameAddress = UInt64(nameOffset) + machoHeaderPtr_Int
if let cChar = UnsafePointer<CChar>.init(bitPattern: Int(nameAddress)) {
print(String(cString: cChar))
}
}
//10.拿到FieldDescriptor的Offset
let fieldDescriptorOffset = tcdOffset + 16 + UInt64(targetclassDescriptorPtr!.pointee.fieldDescriptor)
//11.獲取FieldDescriptor在內(nèi)存中的真實(shí)地址
let fieldDescriptor_Int = fieldDescriptorOffset + machoHeaderPtr_Int
struct FieldDescriptor {
var mangledTypeName: Int32
var superclass: Int32
var Kind: UInt16
var fieldRecordSize: UInt16
var numFields: UInt32
// var fieldRecords: [FieldRecord]
}
struct FieldRecord{
var Flags: UInt32
var mangledTypeName: Int32
var fieldName: UInt32
}
//12.將FiledDescriptor真實(shí)內(nèi)存地址綁定到結(jié)構(gòu)體指針上
let fileDescriptorPtr = UnsafePointer<FieldDescriptor>.init(bitPattern: UInt(fieldDescriptor_Int))
//13.獲取FieldRecords在內(nèi)存中的真實(shí)地址并綁定到結(jié)構(gòu)體上
let fileRecord_Int = fieldDescriptor_Int + 16
//14.便利打印屬性名稱
for i in 0..<fileDescriptorPtr!.pointee.numFields {
let fileRecordPtr = UnsafePointer<FieldRecord>.init(bitPattern: UInt(fileRecord_Int + UInt64(i) * 12))
let fileName_Int = UInt64(fileRecord_Int + UInt64(i) * 12) + 8 + UInt64(fileRecordPtr!.pointee.fieldName) - vmAddress
if let cChar = UnsafePointer<CChar>.init(bitPattern: Int(fileName_Int)) {
print(String(cString: cChar))
}
}
//疑問1:通過FileOffsetVectorOffset獲取屬性的值团滥,后續(xù)補(bǔ)充
//通過源碼得出的vTable結(jié)構(gòu)體
struct vTable {
var flags: TargetMethodDescriptor
var methodImpOffset: UInt32
}
struct TargetMethodDescriptor {
var flagType: UInt32
//從源碼恢復(fù)的
enum KindType: UInt32 {
case Method,
Init,
Getter,
Setter,
ModifyCoroutine,
ReadCoroutine
}
enum FlagTypeMask: UInt32 {
case KindMask = 0x0F, // 16 kinds should be enough for anybody
IsInstanceMask = 0x10,
IsDynamicMask = 0x20,
IsAsyncMask = 0x40,
// ExtraDiscriminatorShift = 16, //這個16與0x10沖突了
ExtraDiscriminatorMask = 0xFFFF0000
}
func kindType() -> KindType {
return KindType(rawValue: flagType & FlagTypeMask.KindMask.rawValue)!
}
}
//15.獲取方法(vtable)
//vtable
let vtable_Int = targetClassDescriptor_Int + 13 * 4
let vtableCount = targetclassDescriptorPtr!.pointee.size
//vtable偏移量
var vtableOffset = tcdOffset + 13 * 4
for i in 0 ..< vtableCount {
let vtablePtr = UnsafePointer<vTable>.init(bitPattern: UInt(vtable_Int + UInt64(i) * 8))
//排除添加屬性生成的Getter竿屹、Setter、Modify方法灸姊,init方法
//這里只拿到teach相關(guān)方法
if vtablePtr!.pointee.flags.kindType() == .Method {
let methodImp_Int = vtable_Int + UInt64(i) * 8 + UInt64(vtablePtr!.pointee.methodImpOffset) + 4 - vmAddress
let methodImp = UnsafePointer<UInt64>.init(bitPattern: UInt(methodImp_Int))
//將Imp指針轉(zhuǎn)化為方法
typealias Function = @convention(c) () -> Void
//unsafeBitCast,將一個指針指向的內(nèi)存強(qiáng)制按位轉(zhuǎn)化為目標(biāo)的類型拱燃。也就是將Imp強(qiáng)制轉(zhuǎn)化為Swift中的Function,從而完成調(diào)用力惯。否則在Swift里是不能操作Imp的
let function = unsafeBitCast(methodImp, to: Function.self)
function()
}
}
疑問這里的vtable怎么沒有方法的名稱
/*
方法的名稱稱之為符號碗誉。
方法的名稱是放在Mach-o中的String Table中的。通過Symbol Table去查找String Table
關(guān)于Symbol Table:1.String Table Index:存放了符號在字符串表的偏移量父晶。2.Value:存放了當(dāng)前函數(shù)的地址(也就是函數(shù)的Imp地址)
根據(jù)偏移量去String Table就可以找到函數(shù)的方法名稱
在上架的過程中是會將Symbol Table給抹除掉哮缺。
原因也是因?yàn)槌绦虻陌踩裕谖覀兂绦蜻\(yùn)行的過程中會把符號表給剔除甲喝,mach-o會保留動態(tài)符號表(Dynamic Symbol Table)尝苇。將剔除的符號表放在了.dSYM文件下,也就是使用dSYM還原符號表的由來埠胖。
因?yàn)槲覀兂绦蚓幾g完成后糠溜,函數(shù)的地址就確定了,保留符號是沒有必要的直撤,如果我們保留了符號表非竿,別人可以通過符號表通過符號很輕易的拿到函數(shù)地址信息,從而對這個函數(shù)進(jìn)行hock谋竖,顯然這是不安全的红柱。
*/
驗(yàn)證猜想及結(jié)論
1.從上面的代碼拿到一個方法teach
的Imp0x0000000100003de0
2.Symbol Table
找到該Imp
指向的內(nèi)容
3.通過拿到的0x58FC
去String Table
拿到方法名稱
此時的函數(shù)名稱應(yīng)該存放在0x180A8 + 0x58FC = 0x1D9A4
4.查看0x1D9A4
此時可以發(fā)現(xiàn)這個字符串信息其實(shí)就是在第二步圖中的_$s9swiftTest9LGTeacherC5teachyyF
,此時信息是混淆后的
5.通過終端還原混淆代碼
命令xcrun swift-demangle xxx
- 至此蓖乘,成功還原出
teach符號化信息
四.內(nèi)存綁定
Swift提供了三種不同的Api來綁定/重新綁定指針
assumingMemoryBound(to:)
func testPointer(_ p: UnsafePointer<Int>) {
print(p[0]) // 10
print(p[1]) // 20
}
let tuple = (10, 20)
withUnsafePointer(to: tuple) {
testPointer(UnsafeRawPointer($0).assumingMemoryBound(to: Int.self))
}
/*
assumingMemoryBound使用場景
比如一個方法需要一個類型指針锤悄,但是現(xiàn)有的類型指針或原生指針與之不匹配,并且現(xiàn)有類型指針或原生指針指向的數(shù)據(jù)類型與需要的類型指針一致驱敲,
因此可以使用該方法assumingMemoryBound(UnsafeRaPointer)告訴編譯器當(dāng)前指針指向的數(shù)據(jù)的類型是Int類型铁蹈,不需要再檢查我了宽闲,也就是繞過編譯器众眨,并沒有發(fā)生實(shí)際的類型轉(zhuǎn)換
*/
bindMemory
func testPointer(_ p: UnsafePointer<Int>) {
print(p[0]) // 10
print(p[1]) // 20
}
let tuple = (10, 20)
withUnsafePointer(to: tuple) {
// testPointer(UnsafeRawPointer($0).assumingMemoryBound(to: Int.self))
testPointer(UnsafeRawPointer($0).bindMemory(to: Int.self, capacity: 1)) // UnsafePointer<T>
}
/*
bindMemory
發(fā)生了實(shí)際類型的轉(zhuǎn)化握牧,把當(dāng)前的原生指針轉(zhuǎn)換成了UnsafePointer<T>,改定當(dāng)前內(nèi)存綁定的類型
*/
withMemoryRebound
func testPointer(_ p: UnsafePointer<Int>) {
print(p[0]) // 10
print(p[1]) // 20
}
let tuple = (10, 20)
withUnsafePointer(to: tuple) {
// testPointer(UnsafeRawPointer($0).assumingMemoryBound(to: Int.self))
// testPointer(UnsafeRawPointer($0).bindMemory(to: Int.self, capacity: 1)) // UnsafePointer<T>
$0.withMemoryRebound(to: Int.self, capacity: 1) {
testPointer($0)
}
}
/*
withMemoryRebound
用于臨時更改當(dāng)前指針綁定的內(nèi)存娩梨,出了閉包作用域就失效了
*/
查看源碼通過內(nèi)存綁定
還原實(shí)例
//查看源碼總結(jié)出的MetaData及HeapObject結(jié)構(gòu)
struct MetaData {
var kind: Int
var superClass: Any.Type
var cacheData: (Int,Int)
var data: Int
var flags: UInt32
var instanceAddressPoint: UInt32
var instanceSize: UInt32
var instanceAlignMask: UInt16
var reserved: UInt16
var classSize: UInt32
var classAddressPoint: UInt32
var description: UInt32
var iVarDestroyer: UInt32
}
struct HeapObject {
var metadata: UnsafePointer<MetaData>
var refCount: UInt
}
class LGTeacher {
var age = 18
var name = "Kody"
}
//此時的heapObject就是還原后的數(shù)據(jù)
let heapObject = Unmanaged.passUnretained(t).toOpaque().bindMemory(to: HeapObject.self, capacity: 1)
五.內(nèi)存管理中的引用計(jì)數(shù)(RefCount)
Swift中使用自動引用計(jì)數(shù)(ARC)
class LGTeacher{
var age: Int = 18
var name: String = "Kody"
}
var t = LGTeacher()
//獲取內(nèi)存指針
print(Unmanaged.passUnretained(t).toOpaque())
斷點(diǎn)下在print
0x000000010b50d2f0
(lldb) x/8g 0x000000010b50d2f0
0x10b50d2f0: 0x00000001000081a8 0x0000000000000003
0x10b50d300: 0x0000000000000012 0x0000000079646f4b
0x10b50d310: 0xe400000000000000 0x0000000000000000
0x10b50d320: 0x0000000000000006 0x00007ff850b6d198
(lldb)
-
0x0000000000000003
這個就是refCount
1.源碼分析refCount
1.進(jìn)入HeapObject.h
找到RefCount
// The members of the HeapObject header that are not shared by a
// standard Objective-C instance
#define SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS \
InlineRefCounts refCounts
/// 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;
...
}
2.進(jìn)入InlineRefCounts
typedef RefCounts<InlineRefCountBits> InlineRefCounts;
typedef RefCounts<SideTableRefCountBits> SideTableRefCounts;
template <typename RefCountBits>
class RefCounts {
std::atomic<RefCountBits> refCounts;
// Out-of-line slow paths.
SWIFT_NOINLINE
void incrementSlow(RefCountBits oldbits, uint32_t inc) SWIFT_CC(PreserveMost);
SWIFT_NOINLINE
void incrementNonAtomicSlow(RefCountBits oldbits, uint32_t inc);
SWIFT_NOINLINE
bool tryIncrementSlow(RefCountBits oldbits);
SWIFT_NOINLINE
bool tryIncrementNonAtomicSlow(RefCountBits oldbits);
SWIFT_NOINLINE
void incrementUnownedSlow(uint32_t inc);
...
}
此時的
InlineRefCounts
是RefCounts<InlineRefCountBits>
的模板類沿腰。有一個泛型參數(shù)InlineRefCountBits
在RefCounts類中我們可以發(fā)現(xiàn)其實(shí)都是在操作傳進(jìn)來的泛型參數(shù)
InlineRefCountBits
因此
RefCounts
其實(shí)是對引用計(jì)數(shù)的一個包裝,引用計(jì)數(shù)的具體類型取決于傳入進(jìn)來的具體參數(shù)狈定。
3.探究泛型參數(shù)InlineRefCountBits
//InlineRefCountBits是RefCountBitsT的模板函數(shù)
// RefCountIsInline泛型參數(shù)true或false
typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;
// RefCountIsInline: refcount stored in an object
// RefCountNotInline: refcount stored in an object's side table entry
enum RefCountInlinedness { RefCountNotInline = false, RefCountIsInline = true };
//找到RedCountBitsT
class RefCountBitsT {
friend class RefCountBitsT<RefCountIsInline>;
friend class RefCountBitsT<RefCountNotInline>;
static const RefCountInlinedness Inlinedness = refcountIsInline;
//BitsType是RefCountBitsInt的模板函數(shù)
typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::Type
BitsType;
typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::SignedType
SignedBitsType;
typedef RefCountBitOffsets<sizeof(BitsType)>
Offsets;
//只有一個屬性bits
BitsType bits;
...
}
//找到BitsType的模板函數(shù)RefCountBitsInt
//發(fā)現(xiàn)本質(zhì)就是一個64位的位域信息
// 64-bit inline
// 64-bit out of line
template <RefCountInlinedness refcountIsInline>
struct RefCountBitsInt<refcountIsInline, 8> {
typedef uint64_t Type;
typedef int64_t SignedType;
};
實(shí)際上Swift中的
RefCounts
其實(shí)也是存放的64位 的位域信息
颂龙,存放了運(yùn)行聲明周期相關(guān)的引用計(jì)數(shù)為什么一個簡單的UInt64需要繞這么大一圈來存儲?
因?yàn)槌橄蟪鰜淼哪0搴瘮?shù)后續(xù)還要兼容弱引用纽什,提高代碼復(fù)用率
4.創(chuàng)建一個實(shí)例對象措嵌,當(dāng)前的引用計(jì)數(shù)是多少?
//之前我們在探究Metadata時芦缰,在_swift_allocObject中有實(shí)例的初始化方法
new (object) HeapObject(metadata);
//HeapObject的初始化代碼
// Initialize a HeapObject header as appropriate for a newly-allocated object.
constexpr HeapObject(HeapMetadata const *newMetadata)
: metadata(newMetadata)
, refCounts(InlineRefCounts::Initialized)
{ }
//傳入的RedCount是Initialized
//找到Initialzed的定義企巢,在Class RefCount中
class RefCounts {
std::atomic<RefCountBits> refCounts;
...
public:
//Initialized其實(shí)是一個枚舉Initialized_t
enum Initialized_t { Initialized };
enum Immortal_t { Immortal };
// Refcount of a new object is 1.
constexpr RefCounts(Initialized_t)
//其實(shí)就是傳入0,1
//RefCountBits也就是RefCountBitsT這個類
: refCounts(RefCountBits(0, 1)) {}
...
}
/*
RefCountBits(0, 1)
strongExtraCount - 0
unownedCount - 1
根據(jù)RefCountBitOffsets,得出
StrongExtraRefCountShift = 33
PureSwiftDeallocShift = 0
UnownedRefCountShift = 1
static const size_t StrongExtraRefCountShift = shiftAfterField(IsDeiniting);
static const size_t UnownedRefCountShift = shiftAfterField(PureSwiftDealloc);
# define shiftAfterField(name) (name##Shift + name##BitCount)
把強(qiáng)引用計(jì)數(shù)和無主引用計(jì)數(shù)通過位移的方法存放在64位位域信息當(dāng)中
強(qiáng)引用左偏移33位让蕾,0
是否是純Swift析構(gòu)函數(shù)的標(biāo)志位浪规,左偏移0位,也就是低1位1探孝。因?yàn)檫@里是由swift_allocObject執(zhí)行過來的笋婿,肯定是Swift的析構(gòu)
無主引用左偏移1位,低2位上是1顿颅。也就是0x2
*/
SWIFT_ALWAYS_INLINE
constexpr
RefCountBitsT(uint32_t strongExtraCount, uint32_t unownedCount)
: bits((BitsType(strongExtraCount) << Offsets::StrongExtraRefCountShift) |
(BitsType(1) << Offsets::PureSwiftDeallocShift) |
(BitsType(unownedCount) << Offsets::UnownedRefCountShift))
{ }
位移結(jié)構(gòu)體RefCountBitOffsets
及shiftAfterField
// Layout of refcount bits.
// field value = (bits & mask) >> shift
// FIXME: redo this abstraction more cleanly
# define maskForField(name) (((uint64_t(1)<<name##BitCount)-1) << name##Shift)
# define shiftAfterField(name) (name##Shift + name##BitCount)
template <size_t sizeofPointer>
struct RefCountBitOffsets;
// 64-bit inline
// 64-bit out of line
// 32-bit out of line
template <>
struct RefCountBitOffsets<8> {
/*
The bottom 32 bits (on 64 bit architectures, fewer on 32 bit) of the refcount
field are effectively a union of two different configurations:
---Normal case---
Bit 0: Does this object need to call out to the ObjC runtime for deallocation
Bits 1-31: Unowned refcount
---Immortal case---
All bits set, the object does not deallocate or have a refcount
*/
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);
};
2.總結(jié)出當(dāng)沒有SideTable時的位域信息
源碼版本Swift5.5.2
占位 | 名稱 | 含義 |
---|---|---|
0 | PureSwiftDealloc | 是否使用Swift執(zhí)行Dealloc析構(gòu)函數(shù)缸濒,false表示使用objc-Runtime執(zhí)行析構(gòu)函數(shù) |
1~31 | UnownedRefCount | 無主引用計(jì)數(shù) |
32 | isDeiniting | 是否正在析構(gòu)函數(shù),也可以稱為是否正在釋放 |
33~62 | StrongExtraRefCount | 強(qiáng)引用計(jì)數(shù) |
63 | UseSlowRC | 是否使用緩慢RC |
0~32 | isImmortal | 是否是不朽對象粱腻。由之前的0變?yōu)?~32 |
- 對于之前的
isImmortal
`overlaps PureSwiftDealloc and UnownedRefCount`
根據(jù)绍填,這段是覆蓋在`PureSwiftDealloc`和`UnOwnedRefCount`上的
---Immortal case---
All bits set, the object does not deallocate or have a refcount
如果將所有bits設(shè)置后,這個對象不會釋放或者有一個引用計(jì)數(shù)
3.代碼驗(yàn)證位域信息
- 驗(yàn)證
isDeiniting
class LGTeacher {
var age: Int = 18
var name: String = "Kody"
}
var t: LGTeacher? = LGTeacher()
//獲取內(nèi)存指針
//為什么沒有用po t栖疑,而是使用代碼獲取內(nèi)存地址讨永。是因?yàn)閜o t會影響當(dāng)前的引用計(jì)數(shù)
print(Unmanaged.passUnretained(t!).toOpaque())
//breakpoint 0x10b10f6b0: 0x00000001000081a8 0x0000000000000003
t = nil
//breakpoint 0x10b10f6b0: 0x00000001000081a8 0x0000000100000003
- 當(dāng)執(zhí)行完
t = nil
后,refCount
的32位變?yōu)榱?遇革,表示實(shí)例對象正在執(zhí)行析構(gòu)函數(shù)
2.源碼查看強(qiáng)引用的添加_swift_retain_
SWIFT_ALWAYS_INLINE
static HeapObject *_swift_retain_(HeapObject *object) {
SWIFT_RT_TRACK_INVOCATION(object, swift_retain);
if (isValidPointerForNativeRetain(object))
object->refCounts.increment(1);
return object;
}
//進(jìn)入object->refCounts.increment(1)
// Increment the reference count.
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;
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));
}
//增加強(qiáng)引用計(jì)數(shù)newbits.incrementStrongExtraRefCount(inc)
// Returns true if the increment is a fast-path result.
// Returns false if the increment should fall back to some slow path
// (for example, because UseSlowRC is set or because the refcount overflowed).
SWIFT_NODISCARD SWIFT_ALWAYS_INLINE bool
incrementStrongExtraRefCount(uint32_t inc) {
// This deliberately overflows into the UseSlowRC field.
//這里的inc為1卿闹,相當(dāng)于bits += 1<< 33
//StrongExtraRefCountShift = 33
bits += BitsType(inc) << Offsets::StrongExtraRefCountShift;
return (SignedBitsType(bits) >= 0);
}
- 調(diào)用
swift_retain
相當(dāng)于執(zhí)行bits += inc << 33
,也印證了初始化的邏輯
3.代碼驗(yàn)證強(qiáng)引用計(jì)數(shù)
class LGTeacher {
var age: Int = 18
var name: String = "Kody"
}
var t = LGTeacher()
//獲取內(nèi)存指針
//為什么沒有用po t萝快,而是使用代碼獲取內(nèi)存地址锻霎。是因?yàn)閜o t會影響當(dāng)前的引用計(jì)數(shù)
print(Unmanaged.passUnretained(t).toOpaque())
var t1 = t //breakpoint 0x101423ed0: 0x00000001000081a8 0x0000000000000003(源碼分析0位為1,1位為1存放無主引用,剛好為0x3)
var t2 = t //breakpoint 0x101423ed0: 0x00000001000081a8 0x0000000200000003(源碼分析揪漩,1偏移33位存放強(qiáng)引用旋恼,加上之前的無主引用0x3,為0x200000003)
var t3 = t //breakpoint 0x101423ed0: 0x00000001000081a8 0x0000000400000003(源碼分析奄容,2偏移33位存放強(qiáng)引用冰更,加上之前的無主引用0x3产徊,為0x400000003)
var t4 = t //breakpoint 0x101423ed0: 0x00000001000081a8 0x0000000600000003(源碼分析,3偏移33位存放強(qiáng)引用蜀细,加上之前的無主引用0x3舟铜,為0x600000003)
六.內(nèi)存管理中的弱引用
弱引用不會對其引用的實(shí)例保持強(qiáng)引用,因而不會阻止ARC
釋放被引用的實(shí)例奠衔。這個特性阻止了引用變?yōu)檠h(huán)強(qiáng)引用谆刨。聲明屬性或變量時,在前面加上weak
關(guān)鍵字表明這是一個弱引用归斤。
由于弱引用不會強(qiáng)保持對實(shí)例的引用痊夭,所以說實(shí)例被釋放了弱引用仍舊引用著這個實(shí)例也是有可能的。因此脏里,ARC
會在被引用的實(shí)例被釋放時自動地設(shè)置弱引用為nil
.由于弱引用需要允許它們的值為nil
生兆,它們一定得是可選類型。
簡而言之就是 weak var t = LGTeacher()
膝宁,這里t因?yàn)槭强勺兊模ㄡ尫艜r會變?yōu)閚il)鸦难,所以必須是由var
修飾
1.通過匯編探究使用weak后,程序是怎么執(zhí)行的
class LGTeacher {
var age: Int = 18
var name: String = "Kody"
}
weak var t = LGTeacher() // 添加一個斷點(diǎn)
- 發(fā)現(xiàn)使用了
weak
后會調(diào)用swift_weakInit
函數(shù)
2.通過源碼分析swift_weakInit
WeakReference *swift::swift_weakInit(WeakReference *ref, HeapObject *value) {
ref->nativeInit(value);
return ref;
}
//nativeInit
void nativeInit(HeapObject *object) {
auto side = object ? object->refCounts.formWeakReference() : nullptr;
nativeValue.store(WeakReferenceBits(side), std::memory_order_relaxed);
}
//formWeakReference
// SideTableRefCountBits specialization intentionally does not exist.
template <>
HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::formWeakReference()
{
//創(chuàng)建了一個SideTable
auto side = allocateSideTable(true);
if (side)
return side->incrementWeak();
else
return nullptr;
}
- 聲明一個
var
變量相當(dāng)于重新定義了WeakReference
對象 - 實(shí)際上就是創(chuàng)建了一個散列表
SideTable
3.SideTable相關(guān)分析
進(jìn)入到allocateSideTable
// Return an object's side table, allocating it if necessary.
// Returns null if the object is deiniting.
// SideTableRefCountBits specialization intentionally does not exist.
template <>
HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::allocateSideTable(bool failIfDeiniting)
{
//取出原先的RefCount
auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
// Preflight failures before allocating a new side table.
//判斷原先的refCount是否有SideTable员淫,如果有直接返回SideTable
if (oldbits.hasSideTable()) {
// Already have a side table. Return it.
return oldbits.getSideTable();
}
//如果當(dāng)前實(shí)例正在執(zhí)行析構(gòu)函數(shù)合蔽,直接返回null。也就沒有必要做其它操作了
else if (failIfDeiniting && oldbits.getIsDeiniting()) {
// Already past the start of deinit. Do nothing.
return nullptr;
}
// Preflight passed. Allocate a side table.
// FIXME: custom side table allocator
//通過HeapObjectSideTableEntry創(chuàng)建一個SideTable
HeapObjectSideTableEntry *side = new HeapObjectSideTableEntry(getHeapObject());
//通過side初始化RefCount
auto newbits = InlineRefCountBits(side);
do {
//同上
if (oldbits.hasSideTable()) {
// Already have a side table. Return it and delete ours.
// 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存入之前的RefCount的信息
side->initRefCounts(oldbits);
} while (! refCounts.compare_exchange_weak(oldbits, newbits,
std::memory_order_release,
std::memory_order_relaxed));
return side;
}
進(jìn)入HeapObjectSideTableEntry
//這里的InlineRefCounts是我們在分析介返,執(zhí)行swift_allocObject時拴事,在HeapObject中的refCount類型就是InlineRefCounts。
typedef RefCounts<InlineRefCountBits> InlineRefCounts;
//這里的SideTableRefCounts圣蝎,與InlineRefCounts共用一個模板函數(shù)RefCounts刃宵。
//并且這里的泛型參數(shù)SideTableRefCountBits也是共用一個父類RefCountBitsT
typedef RefCounts<SideTableRefCountBits> SideTableRefCounts;
class HeapObjectSideTableEntry {
// FIXME: does object need to be atomic?
//存儲了當(dāng)前的實(shí)例對象
std::atomic<HeapObject*> object;
//存儲了當(dāng)前的refCount
SideTableRefCounts refCounts;
...
}
關(guān)于SideTableRefCountBits
//與InlineRefCountBits共同繼承自RefCountBits
class alignas(sizeof(void*) * 2) SideTableRefCountBits : public RefCountBitsT<RefCountNotInline>
{
//多了一個32字節(jié)存儲weakBits
uint32_t weakBits;
public:
SWIFT_ALWAYS_INLINE
SideTableRefCountBits() = default;
SWIFT_ALWAYS_INLINE
constexpr SideTableRefCountBits(uint32_t strongExtraCount,
uint32_t unownedCount)
: RefCountBitsT<RefCountNotInline>(strongExtraCount, unownedCount)
// weak refcount starts at 1 on behalf of the unowned count
,
weakBits(1) {}
SWIFT_ALWAYS_INLINE
SideTableRefCountBits(HeapObjectSideTableEntry *side) = delete;
SWIFT_ALWAYS_INLINE
SideTableRefCountBits(InlineRefCountBits newbits)
: RefCountBitsT<RefCountNotInline>(&newbits), weakBits(1) {}
SWIFT_ALWAYS_INLINE
void incrementWeakRefCount() { weakBits++; }
SWIFT_ALWAYS_INLINE
bool decrementWeakRefCount() {
assert(weakBits > 0);
weakBits--;
return weakBits == 0;
}
SWIFT_ALWAYS_INLINE
uint32_t getWeakRefCount() {
return weakBits;
}
// Side table ref count never has a side table of its own.
SWIFT_ALWAYS_INLINE
bool hasSideTable() {
return false;
}
};
關(guān)于源碼對InlineRefCount
和SideTableRefCount
的總結(jié)
Storage layout:
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
}
}
}
Swift中存在兩種引用計(jì)數(shù)
不存在弱引用,InlineRefCounts
存在弱引用徘公,HeapObjectSideTableEntry
在源碼中找到關(guān)于SideTableRefCountBits
的初始化方法
SWIFT_ALWAYS_INLINE
RefCountBitsT(HeapObjectSideTableEntry* side)
//通過RefCountOffsets中可知SideTableUnusedLowBits = 3牲证,也就是將HeapObjectSideTableEntry右移3位存入
//(BitsType(1) << Offsets::UseSlowRCShift),1右移63位关面,還是標(biāo)識是否慢速RC
//(BitsType(1) << Offsets::SideTableMarkShift))坦袍,1右移62位,散列表標(biāo)記
: bits((reinterpret_cast<BitsType>(side) >> Offsets::SideTableUnusedLowBits)
| (BitsType(1) << Offsets::UseSlowRCShift)
| (BitsType(1) << Offsets::SideTableMarkShift))
{
assert(refcountIsInline);
}
當(dāng)存在SideTable
時等太,也就是有弱引用的時候捂齐,64位位域分布情況
4.代碼驗(yàn)證
1.獲取refCount
相關(guān)數(shù)據(jù)
class LGTeacher {
var age: Int = 18
var name: String = "Kody"
}
var t = LGTeacher()
//獲取內(nèi)存指針
//為什么沒有用po t,而是使用代碼獲取內(nèi)存地址缩抡。是因?yàn)閜o t會影響當(dāng)前的引用計(jì)數(shù)
print(Unmanaged.passUnretained(t as AnyObject).toOpaque())
//加入斷點(diǎn)
weak var t1 = t
//加入斷點(diǎn)
2.獲取HeapObjectSideTableEntry
- 將62位(
SideTableMark
)和63位(UseSlowRC
)置為0 - 然后左移3位還原出
HeapObjectSideTableEntry
奠宜,得出0x10075F790
3.查看HeapObjectSideTableEntry
信息
4.為什么添加一個弱引用是2?
這時在代碼里再添加一個weak
引用當(dāng)前t,發(fā)現(xiàn)此時的弱引用計(jì)數(shù)為0x3
压真。猜想大概率是因?yàn)樵?code>SideTableRefBits創(chuàng)建時給了一個初始值1娩嚼。
通過源碼分析
//1.找到添加弱引用時執(zhí)行的代碼,使用incrementWeak增加弱引用
// SideTableRefCountBits specialization intentionally does not exist.
template <>
HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::formWeakReference()
{
auto side = allocateSideTable(true);
if (side)
return side->incrementWeak();
else
return nullptr;
}
//2.HeapObejectSideTableEntry中的incrementWeak()
// WEAK
SWIFT_NODISCARD
HeapObjectSideTableEntry* incrementWeak() {
// incrementWeak need not be atomic w.r.t. concurrent deinit initiation.
// The client can't actually get a reference to the object without
// going through tryRetain(). tryRetain is the one that needs to be
// atomic w.r.t. concurrent deinit initiation.
// The check here is merely an optimization.
if (refCounts.isDeiniting())
return nullptr;
refCounts.incrementWeak();
return this;
}
//3.找到RefCount中的incrementWeak()
// Increment the weak reference count.
void incrementWeak() {
auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
RefCountBits newbits;
do {
newbits = oldbits;
assert(newbits.getWeakRefCount() != 0);
newbits.incrementWeakRefCount();
if (newbits.getWeakRefCount() < oldbits.getWeakRefCount())
swift_abortWeakRetainOverflow();
} while (!refCounts.compare_exchange_weak(oldbits, newbits,
std::memory_order_relaxed));
}
//4.找到incrementWeakRefCount()
SWIFT_ALWAYS_INLINE
void incrementWeakRefCount() { weakBits++; }
//5.weakBits就是存放弱引用的uint32_t類型榴都。 因此就解釋了多一個引用計(jì)數(shù)就變?yōu)榱?
//6.根據(jù) HeapObjectSideTableEntry *side = new HeapObjectSideTableEntry(getHeapObject());待锈,
//找到HeapObjectSideTableEntry的初始化方法
//函數(shù)內(nèi)創(chuàng)建了refCounts漠其,這里的refCounts是SideTableRefCount嘴高,也就是RefCount<SideTableRefCountBits>.
public:
HeapObjectSideTableEntry(HeapObject *newObject)
: object(newObject), refCounts()
{ }
//7.找到SideTableRefCountBits的初始化方法
//在SideTableRefCountBits創(chuàng)建的時候?qū)eakBits設(shè)置為1
SWIFT_ALWAYS_INLINE
constexpr SideTableRefCountBits(uint32_t strongExtraCount,
uint32_t unownedCount)
: RefCountBitsT<RefCountNotInline>(strongExtraCount, unownedCount)
// weak refcount starts at 1 on behalf of the unowned count
,
weakBits(1) {}
5.圖解總結(jié)InlineRefCounts和HeapObjectSideTableEntry
六.內(nèi)存管理中的無主引用
關(guān)鍵字unowned
。和弱引用類似和屎,無主引用不會牢牢保持住引用的實(shí)例拴驮。但是不像弱引用,總之引用假定是永遠(yuǎn)有值的柴信。因此無主引用并不是類型安全的
class LGTeacher {
var age: Int = 18
var name: String = "Kody"
}
var t: LGTeacher? = LGTeacher()
unowned let t2 = t
t = nil
print(t2)
//Fatal error: Attempted to read an unowned reference but object 0x10142c9c0 was already deallocated2022-01-13 17:46:36.434502+0800 swiftTest[2358:153642] Fatal error: Attempted to read an unowned reference but object 0x10142c9c0 was already deallocated
//Fatal error: Attempted to read an unowned reference but object 0x10142c9c0 was already deallocated
根據(jù)蘋果官方文檔的建議套啤。
當(dāng)我們知道兩個對象的生命周期并不相關(guān),那么我們必須使用weak
随常。例如delegate
反正潜沦,非強(qiáng)引用對象擁有和強(qiáng)引用對象擁有同樣的聲明周期的話,我們應(yīng)該使用unowned
unowned
的性能更好绪氛,不需要創(chuàng)建SideTable
七.內(nèi)存管理中的循環(huán)引用&捕獲列表
class LGTeacher {
var age: Int = 18
var name: String = "Kody"
deinit {
print("LGTeacher deinit")
}
}
let t = LGTeacher()
let closure = {
print(t.age)
}
closure()
//18
//并沒有執(zhí)行deinit唆鸡,此時不執(zhí)行的原因并不是因?yàn)檠h(huán)引用,而是因?yàn)閠是一個全局變量
循環(huán)引用例子
class LGTeacher {
var age: Int = 18
var name: String = "Kody"
var closure: (() -> Void)?
deinit {
print("LGTeacher deinit")
}
}
func test() {
let t = LGTeacher()
//這里就形成了循環(huán)引用
t.closure = {
print(t.age)
}
t.closure?()
}
test()
//18
解決方案
class LGTeacher {
var age: Int = 18
var name: String = "Kody"
var closure: (() -> Void)?
deinit {
print("LGTeacher deinit")
}
}
func test() {
let t = LGTeacher()
//這里就形成了循環(huán)引用
weak var t1 = t //使用弱引用解決循環(huán)引用
unowned var t2 = t //使用無主引用解決循環(huán)引用
t.closure = {
print(t1?.age)
}
t.closure?()
}
test()
//Optional(18)
//LGTeacher deinit
在Swift中還可以使用捕獲列表
來解決循環(huán)引用
class LGTeacher {
var age: Int = 18
var name: String = "Kody"
var closure: (() -> Void)?
deinit {
print("LGTeacher deinit")
}
}
func test() {
let t = LGTeacher()
//這里就形成了循環(huán)引用
t.closure = { [weak t] in
//也可以使用[unowned t]
print(t?.age)
}
t.closure?()
}
test()
//Optional(18)
//LGTeacher deinit
關(guān)于捕獲列表
var age = 0
var height = 1.85
/*
對編譯器來說枣察,當(dāng)執(zhí)行閉包時(捕獲列表發(fā)生在閉包調(diào)用的時候)争占,
如果有捕獲列表,就會去函數(shù)上下文找到與之對應(yīng)的值序目,復(fù)制到捕獲列表當(dāng)中臂痕,
因此無論后面怎么修改,在閉包內(nèi)部訪問的值都不會改變.
*/
let closure = { [age] in
print(age) //0
print(height) //1.85
}
age = 10
height = 1.85
closure()
在Switf閉包內(nèi)使用strong dance
class LGTeacher {
var age: Int = 18
var name: String = "Kody"
var closure: (() -> Void)?
deinit {
print("LGTeacher deinit")
}
}
func test() {
let t = LGTeacher()
t.closure = { [weak t] in
//使用strong dance猿涨,在Swift里就是一個解包操作
if let strongT = t {
print(strongT.age)
}
//延遲實(shí)例變量的生命周期
withExtendedLifetime(t) {
}
}
t.closure?()
}
test()