一、指针
1、指针类型
Swift中的指针分为两类:指定数据类型的指针(typed pointer
);未指定数据类型的指针,也叫原生指针(raw pointer
),基本上有以下:
Swift | OC | 备注 |
---|---|---|
unsafePointer<T> | const T * | 指针及所指向的内容都不可变 |
unsafeMutablePointer<T> | T * | 指针及所指向的内存内容均可变 |
unsafeRawPointer | const void * | 指针指向的内存区域未定 |
unsafeMutableRawPointer | void * | 指针指向的内存区域未定 |
unsafeBufferPointer<T> | 指针指向指定类型的连续缓冲区不可变 | |
unsafeMutableBufferPointer<T> | 指针指向指定类型的连续缓冲区可变 | |
unsafeRawBufferPointer | 指针指向未定类型的连续缓冲区不可变 | |
unsafeMutableRawBufferPointer | 指针指向未定类型的连续缓冲区可变 |
2、原始指针的使用
我们先了解一下MemoryLayout <T>
指定类型的内存布局这个枚举类型。
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?
}
成员/方法 | 例子 | 说明 |
---|---|---|
var size: Int | MemoryLayout <Int>.size | 大小 |
var stride: Int | MemoryLayout <Int>.stride | 存储从一个实例到下一个实例所需要的字节数量,也叫步长. |
var alignment: Int | MemoryLayout <Int>. alignment | 指定类型的默认内存对齐 |
func size(ofValue value: T) -> Int | MemoryLayout.size(ofValue: "10.0") | 根据值计算内存大小 |
func stride(ofValue value: T) -> Int | MemoryLayout.stride(ofValue: PSYModel()) | 根据值计算步长 |
func alignment(ofValue value: T) -> Int | MemoryLayout. alignment(ofValue: PSYModel()) | 根据值计算内存对齐字节 |
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类型大小
print(MemoryLayout<PSYModel>.size) // 当前结构体类型的具体大小
print(MemoryLayout<PSYModel>.stride) // 当前结构体实例的步长信息
print(MemoryLayout<PSYModel>.alignment) // 结构体内存对齐
输出:
16
25
32
8
eg:
使用Raw Pointer来存储4个整形的数据。
// 首先申请4个整形数据大小的内存
let p = UnsafeMutableRawPointer.allocate(byteCount: MemoryLayout<Int>.size * 4, alignment: MemoryLayout<Int>. alignment)
// 遍历循环并根据偏移存入数据
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)
// 循环根据偏移p.load读取内存值
for i in 0..<4{
let value = p.load(fromByteOffset: i*8, as: Int.self)
print("index:\(i), value:\(value)")
}
// 释放内存
p.deallocate()
3、泛型指针的使用
泛型指针相对于原生指针,其是指定了当前绑定了具体的类型。在访问泛型指针时并不是像原生指针那样通过store
和load
访问内存,而是通过泛型指针内置属性pointee
访问。
获取到UnsafePointer的方式有两种:
第一种: 通过已知变量获取
使用withUnsafePointer
访问变量内存地址
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
第二种: 直接分配内存
var age = 10
// 分配一块Int类型的内存空间,此时该内存空间并没有被初始化
let tPtr = UnsafeMutablePointer<Int>.allocate(capacity: 1)
// 分配的内存空间初始化
tPtr.initialize(to: age)
// 通过pointee的属性访问内存的值
print(tPtr.pointee)
tPtr.deallocate() // 释放
或
struct PSYModel{
var age: Int
let name: String
var height: Double
}
// 为结构体类型的指定数量的实例分配未初始化的内存。
var strPtr = UnsafeMutablePointer<PSYModel>.allocate(capacity: 5)
// 内存初始化存储
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)
// 将内存空间清空(一般与deallocate成对出现)
strPtr.deinitialize(count: 3)
// 释放
strPtr.deallocate()
内存初始化存储还可以将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))
以上这三中初始化方式是等价的。
deinitialize(count: 3)
是要根据上面初始化的数量,进行清空内存,初始化了 3个 ,count
就传 3
4、内存指针的使用
将一个对象转换成结构体指针,期间因为需要对指针操作,所以需要管理内存,既繁琐有不安全,所以使用Unmanaged(非托管),通过passUnretained(_ value: Instance). toOpaque()
转成指针,并不对引用计数执行+1操作,所以不用管理其内存。相对应的还有一个passRetained(_ value: Instance). toOpaque()
,其会对实例对象的引用计数执行+1操作,所以使用需注意。
输出及分析流程图struct HeapObject{ // 堆内存中的结构体
var metadata: UnsafeRawPointer // metadata原生指针
var refcount1: Int32 // Int64, 可拆成两个Int32位
var refcount: Int32
}
struct MetaData{ // metadata的结构体
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()
// 拿到实例的指针,但是对实例的引用计数没有影响(Unmanaged非托管的,将实例对象的转成指针,但是对引用计数不执行+1操作)
let psyRawPtr = Unmanaged.passUnretained(psy as AnyObject).toOpaque()
// 绑定成具体的结构体类型
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、内存绑定
Swift提供了三种不同的API来绑定/重绑定指针
-
assumingMemoryBound(to:)
使用场景:
在处理代码时,只有原是指针(UnsafeRawPointer),没有保留指针类型,但是我们明确知道指针的类型,此时用assumingMemoryBound(to:)
绑定成指定类型。此时编译器不进行此类型匹配检查,所以不需要类型转换。
eg:报错案例
报错案例
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转成原生指针,然后调用assumingMemoryBound(to: T.Type)
绑定成指定类型
bindMemory(to: capacity:)
将内存绑定到指定的类型,并返回一个指向绑定内存的类型化指针。使用bindMemory(to:capacity:)
方法将这个指针引用的内存绑定到类型' T '上。内存必须未初始化或初始化为布局与' T '兼容的类型。如果内存未初始化,则绑定到' 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:)
执行给定的闭包,同时将指定数量的实例临时绑定到指定类型。
当您有一个内存指针绑定到一种类型,而您需要作为另一种类型的实例访问该内存时,请使用此方法。以“T”类型访问内存需要将内存绑定到该类型。一个内存位置一次只能绑定到一种类型,因此访问与不相关的类型相同的内存而不首先重新绑定该内存是没有定义的。
从这个指针开始的内存区域,包括指针的' Pointee '类型的' count '实例必须被初始化。
// 一个指向指定类型的已初始化指针
let uint64Pointer: UnsafePointer<UInt64> = 初始值
// 临时重绑定到Int64类型作为临时使用
let isNegative = uint64Pointer.withMemoryRebound(to: Int64.self, capacity: 1) { ptr in
return ptr.pointee < 0
}
二、内存管理
Swift中使用自动引用计数机制(ARC)来管理内存。那就离不开引用计数refCounts
,用代码创建一个实例,看一下其内存引用计数是多少。
查看源码看看HeapObject
结构体中引用计数Refcount是如何定义的:
按住command + 单击
可查看其定义和引用的地方,可发现其是一个模板类typedef RefCounts<InlineRefCountBits> InlineRefCounts;
其是RefCounts
类,接收的是InlineRefCountBits
而RefCounts
类接收的InlineRefCountBits
,其定义是typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits
,是RefCountBitsT
基于这个类接收RefCountIsInline
,而找到RefCountIsInline
其实是一个枚举,如果RefCountIsInline = true
,则引用计数采用一定的编码算法存储在对象中;如果RefCountNotInline = false
,则引用计数存储在散列表中。
再从一个对象初始化分配内存的时候引用计数到底做了啥?
_swift_allocObject_ image.png RefCounts初始化化
可找到初始化一个对象的引用数是1:constexpr RefCounts(Initialized_t) : refCounts(RefCountBits(0, 1)) {}
,其接收的是RefCountBits
这个类型,正是该方法RefCounts
类接收的类型:
所以此时找到
RefCountBitsT
类的初始化方法:RefCountBitsT初始化方法
其中的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)
{ }
再根据传入的初始值,strongExtraCount = 0,unownedCount = 1
constexpr
RefCountBitsT(0, 1) : bits((0 << 33 | (1 << 0) | (1 << 1){ }
0 << 33 = 0
1 << 0 = 1
1 << 1 = 2
最终结果是:0 | 1 | 2 = 3
跟代码运行的结果 2 不匹配,个人认为应该是系统版本和Xcode版本不匹配导致的(笔者Mac OS 10.14.6,Xcode 11.3,而笔者Swift是最新的5.6),找了以前的源码,初始化方法中并没有(BitsType(1) << 0)
,也就少了1,最终的值也就是 2,真好和我当前的Xcode测试结果一致。
1、强引用
强引用到底是如何对Refcount操作呢,笔者通过汇编发现其实际调用的是swift_retain
方法:
然后找到源码,实际是调用对象的属性refCounts的increment
方法:object->refCounts.increment(1);
// 引用计数加 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, 调用incrementStrongExtraRefCount(1)函数
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)函数,返回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
所以上面示例打印内存得到的引用计数的值就可以得到验证了
image.png然而强引用会带来的风险是,如果A强引用B,B强引用A(你中有我,我中有你),就造成了循环引用,导致的直接后果是对象得不到释放,导致内存泄漏,最终可能导致内存溢出。
eg:典型的 “我中有你,你中有我” 循环引用案例
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中解决循环引用的两种方式:使用弱引用(weak reference
)和无主引用(unowned reference
)。
2、弱引用
所以在声明属性或实例变量时,在前面加上weak
关键字表明其实一个弱引用。弱引用并不会强持有实例对象,在ARC中,实例对象被释放的时候,弱引用会被置空为nil(可选类型),这样就不会导致内存泄漏。
class PSYModel{
var age: Int = 19
let name: String = "PSY"
}
weak var psy = PSYModel()
print("end")
调用swift_weakInit
全局搜索源码:
swift_weakInit方法 WeakReference类
swift_weakInit
里面调用了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
// 如果有必要,创建并返回对象的散列表.
// 如果对象正在销毁,返回空.
template <>
HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::allocateSideTable(bool failIfDeiniting)
{
auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
// 做一些必要的预处理
if (oldbits.hasSideTable()) { // 如果已存在散列表直接返回.
return oldbits.getSideTable();
}
else if (failIfDeiniting && oldbits.getIsDeiniting()) { // 如果对象正在销毁,返回空.
return nullptr;
}
// FIXME: 创建散列表
HeapObjectSideTableEntry *side = new HeapObjectSideTableEntry(getHeapObject());
// 初始化
auto newbits = InlineRefCountBits(side);
do {
if (oldbits.hasSideTable()) {
// 如果旧的有散列表,返回它,并删除新创建的。
// 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
同样像强引用一样找到RefCountBitsT以一个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 相当于最高位和次高位是1,从[0~63]这64位最高前两位是1
sild >> 3 表示第三位清空
OK,至此来个案例验证一下:
3、Unowned(无主引用)
类似弱引用,其不会牢牢持有实例,无主引用嘉定是永远有值。
image.png image.png
弱引用与无主引用什么时候用呢?
如果两个对象的生命周期完全和对方没有关系,用weak
如果其中一个对象销毁,另一个对象也跟着销毁,可以用unowned
费强引用对象拥有和强引用对象同样的或者更长的声明周期,用unowned。
总结
-
一个对象在概念上有三种引用。这些refcounts要么存储在isa后面的字段中
inLine
,要么存储在isa后面的字段所指向的side table entry (散列表)
中。 -
强引用:
强RC计数对象的强引用(strong references
)。当强RC达到零时,对象被销毁,无主引用(unowned
)读取将出现错误,而弱引用(weak reference
)读取将变成 nil 。强RC被存储为一个额外的计数:物理字段为0
,逻辑值为1
。 -
无主引用:
无主RC计算对该对象的无主引用。无主RC也有一个额外的+1代表强引用;这个+1在deinit
完成后减少。当无主RC达到零时,该对象的分配将被释放。 -
弱引用:
弱RC计数对象的弱引用。弱RC也有一个额外的+1代表无主引用;这个+1在对象的分配被释放后递减。当弱RC达到0时,对象的散列表将被释放。
-
对象开始时是没有散列表的。当以下情形的时候散列表生成:
- 弱引用被形成,以及有
pending future implementation
- 强引用溢出或无主引用溢出(内联引用在32位系统上是很小的)
3.一个对象需要关联对象存储时
获取散列表是一种单向操作,具有散列表的对象永远不会丢失它。这可以防止一些线程竞争。
- 弱引用被形成,以及有
强的和无主的变量指向这个对象。弱变量指向对象的散列表。
存储布局:
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
}
}
}
对象的生命周期状态:
LIVE without side table
对象的引用初始化为1个强引用,1个无主引用,1个弱引用
没有散列表,没有弱引用存储
强变量和无主变量正常工作
弱变量load不会发生
弱变量存储添加了散列表,成为LIVE with side table
当强引用计数达到0时,调用deinit()
同时对象成为正在DEINITING状态
LIVE with side table
弱变量正常工作,其他的和LIVE一样
DEINITING without side table
Deinit()正在处理该对象。
强变量操作没有效果。
在swift_abortretainun()
中,无主变量加载停止。
无主变量存储正常工作。弱可变load不会发生。弱变量store存储nil。
当deinit()
完成时,生成的代码调用swift_deallocObject
。而swift_deallocObject
调用canBeFreedNow()
快速检查没有弱的或无主的引用。如果canBeFreedNow()
对象被释放并且它变成死的。否则,它会对无主RC进行减量,使该对象变成DEINITED。
DEINITING with side table
弱变量加载返回nil。弱变量store存储nil。
canBeFreedNow()
总是false,所以它永远不会直接过渡到DEAD。
其他的都和DEINITING一样。
DEINITED without side table
Deinit()
已经完成,但是还有一些无主引用。强变量操作不会发生。无主变量存储不会发生。
在swift_abortretainun()
中,无主变量加载停止。弱变量操作不会发生。当无主RC的值为零时,该对象将被释放,并且它将成为DEAD状态。
DEINITED with side table
弱变量加载返回nil。弱变量存储不会发生。
当无主引用变为零时,该对象将被释放,而弱引用将被释放递减,对象变成FREED。其他的都和DEINITED一样。
FREED without side table
这个状态不可能发生
FREED with side table
对象被释放,但对散列表有弱引用未被释放。
强变量操作不会发生。
无主的变量操作不会发生。
弱变量加载返回nil。
弱变量存储不会发生。
当弱引用达到零时,将释放散列表,对象变成DEAD。
DEAD
对象和对象的散列表都没有了
网友评论