本文主要介紹Swift中的內(nèi)存管理,涉及引用計數(shù)、弱引用卵皂、強(qiáng)引用、循環(huán)引用與Runtime等砚亭。
內(nèi)存管理-強(qiáng)引用
在Swift中也是使用ARC來追蹤和管理內(nèi)存的灯变,下面我們通過一個案例來進(jìn)行分析
class SunriseTeacher {
var age: Int = 18
var name: String = "Sunrise"
}
var t = SunriseTeacher()
var t1 = t
var t2 = t
- 查看t的內(nèi)存情況,為什么其中的refCounts是
0x0000000600000003
在分析類時(參考這篇文章Swift進(jìn)階 02:類捅膘、對象添祸、屬性)有這么一個類HeapObject
,下面繼續(xù)通過這個類來分析t的引用計數(shù)
- 分析源碼
HeapObject -> InlineRefCounts
struct HeapObject {
HeapMetadata const *metadata;
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
...
}
??
#define SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS
InlineRefCounts refCounts
- 進(jìn)入
InlineRefCounts
定義寻仗,是RefCounts
類型的別名刃泌,而RefCounts
是模板類
,真正決定的是傳入的類型InlineRefCountBits
typedef RefCounts<InlineRefCountBits> InlineRefCounts;
??
template <typename RefCountBits>
class RefCounts {
std::atomic<RefCountBits> refCounts;
...
}
- 分析
InlineRefCountBits
愧沟,是RefCountBitsT
類的別名
typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;
- 分析
RefCountBitsT
蔬咬,有bits
屬性
template <RefCountInlinedness refcountIsInline>
class RefCountBitsT {
...
typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::Type
BitsType;
...
BitsType bits;
...
}
??
template <>
struct RefCountBitsInt<RefCountNotInline, 4> {
//類型
typedef uint64_t Type;
typedef int64_t SignedType;
};
其中bits
的實質(zhì)是將RefCountBitsInt
中的type
屬性取了一個別名鲤遥,所以bits
的真正類型是uint64_t
即64
位整型數(shù)組
然后來繼續(xù)分析Swift中對象創(chuàng)建的底層方法swift_allocObject
- 分析初始化源碼
swift_allocObject
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
size_t requiredSize,
size_t requiredAlignmentMask) {
...
new (object) HeapObject(metadata);
...
}
??
<!--構(gòu)造函數(shù)-->
constexpr HeapObject(HeapMetadata const *newMetadata)
: metadata(newMetadata)
, refCounts(InlineRefCounts::Initialized)
{ }
- 進(jìn)入
Initialized
定義沐寺,是一個枚舉,其對應(yīng)的refCounts
方法中的
enum Initialized_t { Initialized };
// 對應(yīng)的RefCounts方法
// Refcount of a new object is 1.
constexpr RefCounts(Initialized_t)
: refCounts(RefCountBits(0, 1)) {}
從這里看出真正干事的是RefCountBits
- 進(jìn)入
RefCountBits
定義盖奈,也是一個模板定義
template <typename RefCountBits>
class RefCounts {
std::atomic<RefCountBits> refCounts;
...
}
所以真正的初始化地方是下面這個混坞,實際上是做了一個位域
操作,根據(jù)的是Offsets
LLVM_ATTRIBUTE_ALWAYS_INLINE
constexpr
RefCountBitsT(uint32_t strongExtraCount, uint32_t unownedCount)
: bits((BitsType(strongExtraCount) << Offsets::StrongExtraRefCountShift) |
(BitsType(1) << Offsets::PureSwiftDeallocShift) |
(BitsType(unownedCount) << Offsets::UnownedRefCountShift))
{ }
分析RefCountsBit
的結(jié)構(gòu)钢坦,如下所示
- isImmortal(0)
- UnownedRefCount(1-31): unowned的引用計數(shù)
- isDeinitingMask(32):是否進(jìn)行釋放操作
- StrongExtraRefCount(33-62): 強(qiáng)引用計數(shù)
- UseSlowRC(63
重點關(guān)注UnownedRefCount
和StrongExtraRefCount
- 將t的
refCounts
用二進(jìn)制展示究孕,其中強(qiáng)引用計數(shù)為3
分析SIL代碼
- 當(dāng)只有t實例變量時
- 當(dāng)有t與t1時,查看是否有
strong_retain
操作
alloc_global @main.t1 : main.SunriseTeacher // id: %8
%9 = global_addr @main.t1 : main.SunriseTeacher : $*SunriseTeacher // user: %11
%10 = begin_access [read] [dynamic] %3 : $*SunriseTeacher // users: %12, %11
copy_addr %10 to [initialization] %9 : $*SunriseTeacher // id: %11
// 其中copy_addr等價于
- %new = load $* SunriseTeacher
- strong_retain %new
- store %new to %9
SIL官方文檔中關(guān)于copy_addr的解釋如下
- 其中的
strong_retain
對應(yīng)的就是swift_retain
爹凹,其內(nèi)部是一個宏定義厨诸,內(nèi)部是_swift_retain_
,其實現(xiàn)是對object
的引用計數(shù)作+1
操作
// 內(nèi)部是一個宏定義
HeapObject *swift::swift_retain(HeapObject *object) {
CALL_IMPL(swift_retain, (object));
}
??
// 本質(zhì)調(diào)用的就是 _swift_retain_
static HeapObject *_swift_retain_(HeapObject *object) {
SWIFT_RT_TRACK_INVOCATION(object, swift_retain);
if (isValidPointerForNativeRetain(object))
object->refCounts.increment(1);
return object;
}
??
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;
}
// 64位bits
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));
}
- 回退到
HeapObject
禾酱,從InlineRefCounts
進(jìn)入微酬,其中是c++中的模板定義绘趋,是為了更好的抽象,在其中查找bits
(即decrementStrongExtraRefCount
方法)
LLVM_NODISCARD LLVM_ATTRIBUTE_ALWAYS_INLINE
bool incrementStrongExtraRefCount(uint32_t inc) {
// This deliberately overflows into the UseSlowRC field.
// 對inc做強(qiáng)制類型轉(zhuǎn)換為 BitsType
// 其中 BitsType(inc) << Offsets::StrongExtraRefCountShift 等價于 1<<33位颗管,16進(jìn)制為 0x200000000
// 這里的 bits += 0x200000000陷遮,將對應(yīng)的33-63轉(zhuǎn)換為10進(jìn)制,為
bits += BitsType(inc) << Offsets::StrongExtraRefCountShift;
return (SignedBitsType(bits) >= 0);
}
例如以t
的refCounts
為例(其中62-33
位是strongCount
垦江,每次增加強(qiáng)引用計數(shù)
增加都是在33-62
位上增加的帽馋,固定的增量為1左移33位
,即0x200000000
)
- 只有
t
時的refCounts
是0x0000000200000003
-
t 與 t1
時的refCounts·是 ·0x0000000400000003 = 0x0000000200000003 + 0x200000000
-
t 比吭、 t1 绽族、 t2
時的refCounts
是0x0000000600000003 = 0x0000000400000003 + 0x200000000
針對上面的例子,可以通過CFGetRetainCount
獲取引用計數(shù),發(fā)現(xiàn)依次是 2衩藤、3项秉、4,默認(rèn)多了一個1
- 如果將t慷彤、t1娄蔼、t2放入函數(shù)中,還會再次retain一次
為什么是0x200000000
底哗?
因為1左移33位岁诉,其中4位為一組,計算成16進(jìn)制跋选,剩余的33-32位0x10
涕癣,轉(zhuǎn)換為10進(jìn)制為2
。其實際增加引用計數(shù)就是1
swift與OC強(qiáng)引用計數(shù)對比
- OC中創(chuàng)建實例對象時為
0
- Swift中創(chuàng)建實例對象時默認(rèn)為
1
內(nèi)存管理 - 弱引用
以下面為例:
class SunriseTeacher {
var age: Int = 18
var name: String = "Sunrise"
var student:SunriseStudent?
}
class SunriseStudent {
var age = 20
var teacher: SunriseTeacher?
}
func test() {
var t = SunriseTeacher()
weak var t1 = t
print("end")
}
test()
- 查看
t
的引用計數(shù)變化
- 弱引用聲明的變量是一個
可選值
前标,因為在程序運(yùn)行過程中是允許將當(dāng)前變量設(shè)置為nil
的 - 在
t1
處加斷點坠韩,查看匯編
- 查看
swift_weakInit
函數(shù),這個函數(shù)是由WeakReference
來調(diào)用的炼列,相當(dāng)于weak
字段在編譯器聲明過程中就自定義了一個WeakReference
的對象只搁,其目的在于管理弱引用
WeakReference *swift::swift_weakInit(WeakReference *ref, HeapObject *value) {
ref->nativeInit(value);
return ref;
}
- 進(jìn)入
nativeInit
void nativeInit(HeapObject *object) {
auto side = object ? object->refCounts.formWeakReference() : nullptr;
nativeValue.store(WeakReferenceBits(side), std::memory_order_relaxed);
}
- 進(jìn)入
formWeakReference
,創(chuàng)建sideTable
template <>
HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::formWeakReference()
{
// 創(chuàng)建 sideTable
auto side = allocateSideTable(true);
if (side)
// 如果創(chuàng)建成功俭尖,則增加弱引用
return side->incrementWeak();
else
return nullptr;
}
- 進(jìn)入
allocateSideTable
template <>
HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::allocateSideTable(bool failIfDeiniting)
{
// 1氢惋、先拿到原本的引用計數(shù)
auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
// Preflight failures before allocating a new side table.
if (oldbits.hasSideTable()) {
// Already have a side table. Return it.
return oldbits.getSideTable();
}
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
// 2、創(chuàng)建sideTable
HeapObjectSideTableEntry *side = new HeapObjectSideTableEntry(getHeapObject());
// 3稽犁、將創(chuàng)建的地址給到InlineRefCountBits
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->initRefCounts(oldbits);
} while (! refCounts.compare_exchange_weak(oldbits, newbits,
std::memory_order_release,
std::memory_order_relaxed));
return side;
}
- 1焰望、先拿到原本的引用計數(shù)
- 2、創(chuàng)建
sideTable
- 3已亥、將創(chuàng)建的
sideTable
地址給InlineRefCountBits
熊赖,并查看其初始化方法,根據(jù)sideTable
地址作了偏移操作并存儲到內(nèi)存虑椎,相當(dāng)于將sideTable
直接存儲到了64位
的變量中
所以上面的0xc000000020809a6c
是HeapObjectSideTableEntry
實例對象的內(nèi)存地址震鹉,即散列表的地址
(除去63的妖、62位)
- 查看
HeapObjectSideTableEntry
定義,其中有object
對象足陨、refCounts
- 進(jìn)入
SideTableRefCounts
嫂粟,同InlineRefCounts
類似,實際做事的是SideTableRefCountBits
墨缘,繼承自RefCountBitsT
(存的是uint64_t類型的64位的信息)星虹,還有一個uint32_t
的weakBits
,即32位的位域信息- 64位 用于記錄 原有引用計數(shù)
- 32位 用于記錄 弱引用計數(shù)
以0xc0000000200e9cf8
為例镊讼,將62宽涌、63位清零,變成0x200E9CF8
蝶棋,然后左移3
位(即InlineRefCountBits
初始化方法)卸亮,變成0x10074E7C0
即HeapObjectSideTableEntry對象地址,即散列表地址
玩裙,然后通過x/8g
讀取
問題:如果此時再加一個強(qiáng)引用t2
查看其refCounts兼贸,t2是執(zhí)行了strong_retain的
- 源碼查看
_swift_retain_ -> increment -> incrementSlow -> incrementStrong
總結(jié)
對于HeapObject
來說,其refCounts
有兩種:
- 無弱引用:
strongCount + unownedCount
- 有弱引用:
object + xxx + (strongCount + unownedCount) + weakCount
HeapObject {
InlineRefCountBit {strong count + unowned count }
HeapObjectSideTableEntry{
HeapObject *object
xxx
strong Count + unowned Count(uint64_t)//64位
weak count(uint32_t)//32位
}
}
內(nèi)存管理 - 循環(huán)引用
主要是研究閉包捕獲外部變量
吃溅,以下面代碼為例
var age = 10
let clourse = {
age += 1
}
clourse()
print(age)
// ********** 打印結(jié)果 **********
11
從輸出結(jié)果中可以看出:閉包內(nèi)部對變量的修改將會改變外部原始變量的值
溶诞,主要原因是閉包會捕獲外部變量
,這個與OC中的block是一致的
- 定義一個類决侈,在test函數(shù)作用域消失后螺垢,會執(zhí)行init
class SunriseTeacher {
var age = 18
// 反初始化器(當(dāng)前實例對象即將被回收)
deinit {
print("SunriseTeacher deinit")
}
}
func test(){
var t = SunriseTeacher()
}
test()
// ********** 打印結(jié)果 **********
SunriseTeacher deinit
- 修改例子,通過閉包修改其屬性值
class SunriseTeacher {
var age = 18
// 反初始化器(當(dāng)前實例對象即將被回收)
deinit {
print("SunriseTeacher deinit")
}
}
var t = SunriseTeacher()
let clourse = {
t.age += 1
}
clourse()
print(t.age)
// ********** 打印結(jié)果 **********
19
- 【修改1】將上面例子修改為如下赖歌,其中閉包是否對t有強(qiáng)引用枉圃?
class SunriseTeacher {
var age = 18
// 反初始化器(當(dāng)前實例對象即將被回收)
deinit {
print("SunriseTeacher deinit")
}
}
func test() {
var t = SunriseTeacher()
let clourse = {
t.age += 1
}
clourse()
print(t.age)
}
test()
// ********** 打印結(jié)果 **********
19
運(yùn)行結(jié)果發(fā)現(xiàn),閉包對 t 并沒有強(qiáng)引用
- 【修改2】繼續(xù)修改例子為如下庐冯,是否有強(qiáng)引用孽亲?
class SunriseTeacher {
var age = 18
var completionBlock:(() -> ())?
// 反初始化器(當(dāng)前實例對象即將被回收)
deinit {
print("SunriseTeacher deinit")
}
}
func test() {
var t = SunriseTeacher()
t.completionBlock = {
t.age += 1
}
}
test()
從運(yùn)行結(jié)果發(fā)現(xiàn),沒有執(zhí)行deinit
方法肄扎,即沒有打印SunriseTeacher deinit
墨林,所以這里有循環(huán)引用
循環(huán)引用解決方法
有兩種方式可以解決Swift中的循環(huán)引用
- 【方式一】使用
weak
修飾閉包傳入的參數(shù)赁酝,其中參數(shù)的類型是optional
func test(){
var t = SunriseTeacher()
t.completionBlock = { [weak t] in
t?.age += 1
}
}
- 【方式二】使用
unowned
修飾閉包參數(shù)犯祠,與weak
的區(qū)別在于unowned
不允許被設(shè)置為nil
,即總是假定有值
的
var t = SunriseTeacher()
t.completionBlock = { [unowned t] in
t.age += 1
}
捕獲列表
-
[weak t] / [unowned t]
在Swift中被稱為捕獲列表
- 定義在參數(shù)
列表之前
- 【書寫方式】捕獲列表被寫成用逗號括起來的表達(dá)式列表酌呆,并
用方括號括起來
- 如果使用捕獲列表衡载,則即使省略參數(shù)名稱、參數(shù)類型和返回類型隙袁,也必須使用
in關(guān)鍵字
-
[weak t]
就是取t的弱引用對象 類似weakSelf
請問下面代碼的clourse()
調(diào)用后痰娱,輸出的結(jié)果是什么弃榨?
func test(){
var age = 0
var height = 0.0
// 將變量age用來初始化捕獲列表中的常量age,即將0給了閉包中的age(值拷貝)
let clourse = {[age] in
print(age)
print(height)
}
age = 10
height = 1.85
clourse()
}
// ********** 打印結(jié)果 **********
0
1.85
所以從結(jié)果中可以得出:對于捕獲列表
中的每個常量
梨睁,閉包會利用周圍范圍內(nèi)具有相同名稱的常量/變量
鲸睛,來初始化捕獲列表中定義的常量。有以下幾點說明:
- 捕獲列表中的常量是
值拷貝
坡贺,而不是引用 - 捕獲列表中的常量相當(dāng)于復(fù)制了變量age的值
- 捕獲列表中的常量是只讀的官辈,即不可修改
Swift中Runtime探索
請問下面代碼,會打印方法和屬性嗎遍坟?
class SunriseTeacher {
var age: Int = 18
func teach(){
print("teach")
}
}
let t = SunriseTeacher()
func test(){
var methodCount: UInt32 = 0
let methodList = class_copyMethodList(SunriseTeacher.self, &methodCount)
for i in 0..<numericCast(methodCount) {
if let method = methodList?[I]{
let methodName = method_getName(method)
print("方法列表:\(methodName)")
}else{
print("not found method")
}
}
var count: UInt32 = 0
let proList = class_copyPropertyList(SunriseTeacher.self, &count)
for i in 0..<numericCast(count) {
if let property = proList?[I]{
let propertyName = property_getName(property)
print("屬性成員屬性:\(property)")
}else{
print("沒有找到你要的屬性")
}
}
print("test run")
}
test()
運(yùn)行結(jié)果如下拳亿,發(fā)現(xiàn)并沒有打印方法和屬性
- 【嘗試1】如果給屬性 和 方法 都加上 @objc,可以打印嗎愿伴?
從運(yùn)行結(jié)果看肺魁,是可以打印,但是由于類并沒有暴露給OC隔节,所以O(shè)C是無法使用的鹅经,這樣做是沒有意義的
- 【嘗試2】如果swift的類
繼承NSObject
,沒有@objc修飾屬性和方法怎诫,是否可以打印全部屬性+方法瞬雹?
從結(jié)果發(fā)現(xiàn)獲取的只有init
方法,主要是因為在swift.h
文件中暴露出來的只有init
方法
- 如果想讓OC能使用刽虹,必須類
繼承NSObject + @objc修飾
屬性酗捌、方法
- 如果
去掉@objc
修飾屬性,將方法改成dynamic
修飾涌哲,是否可以打印方法胖缤?
從結(jié)果可以看出,依舊不能被OC獲取到阀圾,需要修改為@objc dynamic
修飾
結(jié)論
- 對于純Swift類來說哪廓,沒有
動態(tài)特性dynamic
(因為Swift
是靜態(tài)
語言),方法和屬性不加任何修飾符的情況下初烘,已經(jīng)不具備runtime
特性涡真,此時的方法調(diào)度,依舊是函數(shù)表調(diào)度即V_Table調(diào)度
- 對于純Swift類肾筐,方法和屬性添加
@objc標(biāo)識
的情況下哆料,可以通過runtime的API獲取到,但是在OC中是無法進(jìn)行調(diào)度的吗铐,原因是因為swift.h
文件中沒有Swift類的聲明 - 對于
繼承自NSObject
類來說东亦,如果想要動態(tài)的獲取當(dāng)前屬性+方法,必須在其聲明前
添加@objc
關(guān)鍵字唬渗,如果想要使用方法交換
典阵,還必須在屬性+方法前
添加dynamic
關(guān)鍵字奋渔,否則當(dāng)前屬性+方法只是暴露給OC使用,而不具備任何動態(tài)特性
objc源碼驗證
由于xcode12.2暫時無法運(yùn)行objc源碼壮啊,下列驗證圖片僅供參考
- 進(jìn)入
class_copyMethodList
源碼嫉鲸,斷住,查看此時的cls
歹啼,其中data()
存儲類的信息
- 進(jìn)入
data
充坑,打印bits、superclass
從這里可以得出Swift
中有默認(rèn)基類染突,即_SwiftObject
- 打印methods
- Swift源碼中搜索
_SwiftObject
刀闷,繼承自NSObject
部默,在內(nèi)存結(jié)構(gòu)上與OC基本類似的
#if __has_attribute(objc_root_class)
__attribute__((__objc_root_class__))
#endif
SWIFT_RUNTIME_EXPORT @interface SwiftObject<NSObject> {
@private
Class isa;
//refCounts
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
}
- 在之前的文章中Swift進(jìn)階 02:類傲须、對象撇贺、屬性,其中
TargetAnyClassMetadata
繼承自TargetHeapMetaData
司志,其中只有一個屬性kind甜紫,TargetAnyClassMetadata
有四個屬性:isa、superclass骂远、cacheData囚霸、data
即bits
所以Swift為了保留和OC交互,其在底層存儲的數(shù)據(jù)結(jié)構(gòu)上和OC是一致的
- objc源碼中搜索swift_class_t激才,繼承自objc_class拓型,保留了OC模板類的4個屬性,其次才是自己的屬性
struct swift_class_t : objc_class {
uint32_t flags;
uint32_t instanceAddressOffset;
uint32_t instanceSize;
uint16_t instanceAlignMask;
uint16_t reserved;
uint32_t classSize;
uint32_t classAddressOffset;
void *description;
// ...
void *baseAddress() {
return (void *)((uint8_t *)this - classAddressOffset);
}
};
問題:為什么繼承NSObject瘸恼?
必須通過NSObject聲明劣挫,來幫助編譯器判斷,當(dāng)前類是一個和OC交互的類
元類型东帅、AnyClass压固、Self
AnyObject
-
AnyObject
:代表任意類的instance
、類的類型
靠闭、僅類遵守的協(xié)議
class SunriseTeacher: NSObject {
var age: Int = 18
}
var t = SunriseTeacher()
// 此時代表的就是當(dāng)前SunriseTeacher的實例對象
var t1: AnyObject = t
// 此時代表的是SunriseTeacher這個類的類型
var t2: AnyObject = SunriseTeacher.self
// 繼承自AnyObject帐我,表示JSONMap協(xié)議只有類才可以遵守
protocol JSONMap: AnyObject { }
例如如果是結(jié)構(gòu)體遵守協(xié)議,會報錯
需要將struct修改成class
// 繼承自AnyObject愧膀,表示JSONMap協(xié)議只有類才可以遵守
protocol JSONMap: AnyObject { }
class SunriseJSONMap: JSONMap {
}
Any
-
Any
:代表任意類型
拦键,包括function類型
或者Optional類型
,可以理解為AnyObject
是Any的子集
// 如果使用AnyObject會報錯扇调,而Any不會
var array: [Any] = [1, "wrs", "", true]
AnyClass
-
AnyClass
:代表任意實例的類型
矿咕,類型是AnyObject.Type
- 查看定義,是
public typealias AnyClass = AnyObject.Type
- 查看定義,是
T.self & T.Type
-
T.self
:- 如果T是
實例對象
狼钮,返回的就是它本身
- 如果T是
類
碳柱,那么返回的是MetaData
- 如果T是
-
T.Type
:一種類型
-
T.self
是T.Type
類型
// 此時的self類型是 SunriseTeacher.Type
var t = SunriseTeacher.self
打印結(jié)果如下:
- 查看t1、t2存儲的是什么熬芜?
class SunriseTeacher {
var age: Int = 18
}
var t = SunriseTeacher()
// 實例對象地址:實例對象.self 返回實例對象本身
var t1 = t.self
// 存儲metadata元類型
var t2 = SunriseTeacher.self
print("end")
type(of:)
-
type(of:)
:用來獲取一個值的動態(tài)類型
// demo1
var age = 10 as NSNumber
print(type(of: age))
// ********** 打印結(jié)果 **********
__NSCFNumber
// demo2
// value - static type 靜態(tài)類型:編譯時期確定好的
// type(of:) - dynamic type:Int
var age = 10
// value的靜態(tài)類型就是Any
func test(_ value: Any){
print(type(of: value))
}
test(age)
// ********** 打印結(jié)果 **********
Int
實踐
demo1
請問下面這段代碼的打印結(jié)果是什么莲镣?
class SunriseTeacher {
var age = 18
var double = 1.85
func teach(){
print("SunriseTeacher teach")
}
}
class SunrisePartTimeTeacher: SunriseTeacher {
override func teach() {
print("SunrisePartTimeTeacher teach")
}
}
func test(_ value: SunriseTeacher){
let valueType = type(of: value)
value.teach()
print(value)
}
var t = SunrisePartTimeTeacher()
test(t)
// ********** 打印結(jié)果 **********
SunrisePartTimeTeacher teach
_5_Runtime.SunrisePartTimeTeacher
demo2
請問下面代碼的打印結(jié)果是什么?
protocol TestProtocol {}
class SunriseTeacher: TestProtocol{
var age = 18
var double = 1.85
func teach(){
print("LGTeacher teach")
}
}
func test(_ value: TestProtocol){
let valueType = type(of: value)
print(valueType)
}
var t = SunriseTeacher()
let t1: TestProtocol = SunriseTeacher()
test(t)
test(t1)
// ********** 打印結(jié)果 **********
SunriseTeacher
SunriseTeacher
- 如果將test中參數(shù)的類型修改為泛型涎拉,此時的打印是什么瑞侮?
func test<T>(_ value: T){
let valueType = type(of: value)
print(valueType)
}
// ********** 打印結(jié)果 **********
SunriseTeacher
TestProtocol
從結(jié)果中發(fā)現(xiàn),打印并不一致鼓拧,原因是因為當(dāng)有協(xié)議半火、泛型
時,當(dāng)前的編譯器并不能推斷出準(zhǔn)確的類型季俩,需要將value轉(zhuǎn)換為Any
钮糖,修改后的代碼如下:
func test<T>(_ value: T){
let valueType = type(of: value as Any)
print(valueType)
}
// ********** 打印結(jié)果 **********
SunriseTeacher
SunriseTeacher
demo3
在上面的案例中,如果class_getClassMethod中傳t.self酌住,可以獲取方法列表嗎店归?
protocol TestProtocol {}
class SunriseTeacher: TestProtocol{
var age = 18
var double = 1.85
func teach(){
print("LGTeacher teach")
}
}
var t = SunriseTeacher()
func test(){
var methodCount: UInt32 = 0
let methodList = class_copyMethodList(t.self, &methodCount)
for i in 0..<numericCast(methodCount) {
if let method = methodList?[i]{
let methodName = method_getName(method)
print("方法列表:\(methodName)")
}else{
print("not found method")
}
}
var count: UInt32 = 0
let proList = class_copyPropertyList(SunriseTeacher.self, &count)
for i in 0..<numericCast(count) {
if let property = proList?[i]{
let propertyName = property_getName(property)
print("屬性成員屬性:\(property)")
}else{
print("沒有找到你要的屬性")
}
}
print("test run")
}
test()
從結(jié)果運(yùn)行看,并不能酪我,因為t.self
是實例對象
本身消痛,即SunriseTeacher
,并不是SunriseTeacher.Type
類型
總結(jié)
- 當(dāng)
無弱引用
時都哭,HeapObject
中的refCounts
等于strongCount + unownedCount
- 當(dāng)
有弱引用
時秩伞,HeapObject中
的refCounts
等于object + xxx + (strongCount + unownedCount) + weakCount
-
循環(huán)應(yīng)用
可以通過weak / unowned
修飾參數(shù)來解決 - Swift中
閉包
的捕獲列表是值拷貝
,即深拷貝
欺矫,是一個只讀的常量 - Swift由于是
靜態(tài)語言
稠歉,所以屬性、方法在不加任何修飾符的情況下時是不具備動態(tài)性
即Runtime特性的汇陆,此時的方法調(diào)度是V-Table函數(shù)表
調(diào)度 - 如果想要
OC使用Swift類
中的方法怒炸、屬性,需要class繼承NSObject
毡代,并使用@objc修飾
- 如果想要使用方法交換阅羹,除了
繼承NSObject+@objc修飾
,還必須使用dynamic修飾
-
Any
:任意類型
教寂,包括function類型捏鱼、optional類型
-
AnyObject
:任意類的instance、類的類型酪耕、僅類遵守的協(xié)議导梆,可以看作是Any的子類
-
AnyClass
:任意實例類型
,類型是AnyObject.Type
-
T.self
:如果T是實例對象,則表示它本身看尼,如果是類递鹉,則表示metadata.T.self
的類型是T.Type