第四節(jié)課:內(nèi)存管理&Runtime
內(nèi)存管理 - 強(qiáng)引用
在swift中也是使用ARC
來(lái)追蹤和管理內(nèi)存的,下面我們先簡(jiǎn)單看一段代碼來(lái)進(jìn)行分析
class HZMTeacher {
var age: Int = 18
var name: String = "HZM"
}
var t = HZMTeacher()
var t1 = t
var t2 = t
- 第一位為
metadata
但是后面的0x0000000600000002
跟我們之前的refCounts
并不一樣
我們分析下源碼 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
其實(shí)質(zhì)是將RefCountBitsInt
中的type
屬性取了一個(gè)別名,所以bits
的真正類型是uint64_t
即64位整型數(shù)組
然后來(lái)繼續(xù)分析swift中對(duì)象創(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
定義,是一個(gè)枚舉,其對(duì)應(yīng)的refCounts
方法中
enum Initialized_t { Initialized };
//對(duì)應(yīng)的RefCounts方法
// Refcount of a new object is 1.
constexpr RefCounts(Initialized_t)
: refCounts(RefCountBits(0, 1)) {}
從這里看出真正干事的是
RefCountBits
進(jìn)入
RefCountBits
定義,也是一個(gè)模板定義
template <typename RefCountBits>
class RefCounts {
std::atomic<RefCountBits> refCounts;
...
}
- 所以真正的初始化地方是下面這個(gè),實(shí)際上是做了一個(gè)位域操作,根據(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的引用計(jì)數(shù)isDeinitingMask
(32):是否進(jìn)行釋放操作StrongExtraRefCount
(33-62): 強(qiáng)引用計(jì)數(shù)UseSlowRC
(63)
重點(diǎn)關(guān)注UnownedRefCount
和StrongExtraRefCount
將t
的refCounts
用二進(jìn)制展示,我們發(fā)現(xiàn):
1~31的UnownedRefCount
為1
33~62的StrongExtraRefCount
為2
當(dāng)只有t實(shí)例變量時(shí)
當(dāng)有t + t1
時(shí),查看是否有 strong_retain
操作
//SIL中的main
alloc_global @main.t1 : main.HZMTeacher // id: %8
%9 = global_addr @main.t1 : main.HZMTeacher : $*HZMTeacher // user: %11
%10 = begin_access [read] [dynamic] %3 : $*HZMTeacher // users: %12, %11
copy_addr %10 to [initialization] %9 : $*HZMTeacher // id: %11
//其中copy_addr等價(jià)于
- %new = load s*HZMTeacher
- strong_retain %new
- store %new to %9
//內(nèi)部是一個(gè)宏定義
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.
// 對(duì)inc做強(qiáng)制類型轉(zhuǎn)換為 BitsType
// 其中 BitsType(inc) << Offsets::StrongExtraRefCountShift 等價(jià)于 1<<33位,16進(jìn)制為 0x200000000
//這里的 bits += 0x200000000,將對(duì)應(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)引用計(jì)數(shù)增加都是在33-62位上增加的,固定的增量為1左移33位
,即0x200000000
)
只有
t
時(shí)的refCounts
是 0x0000000200000002t + t1
時(shí)的refCounts
是 0x0000000400000002 = 0x0000000200000002 + 0x200000000t + t1 + t2
時(shí)的refCounts
是 0x0000000600000002 = 0x0000000400000002 + 0x200000000
針對(duì)上面的例子,可以通過(guò)CFGetRetainCOunt
獲取引用計(jì)數(shù),發(fā)現(xiàn)依次是 2、3、4,默認(rèn)多了一個(gè)1
- 如果將
t、t1、t2
放入函數(shù)中,還會(huì)再次retain一次
內(nèi)存管理07.png
為什么是0x200000000
?
因?yàn)?左移33位,其中4位為一組,計(jì)算成16進(jìn)制,剩余的33-32位0x10
,轉(zhuǎn)換為10進(jìn)制為2。其實(shí)際增加引用計(jì)數(shù)就是1
swift與OC強(qiáng)引用計(jì)數(shù)對(duì)比
OC
中創(chuàng)建實(shí)例對(duì)象時(shí)為0
swift
中創(chuàng)建實(shí)例對(duì)象時(shí)默認(rèn)為1
內(nèi)存管理 - 弱引用
我們先看下面的代碼
class HZMTeacher{
var age: Int = 20
var name: String = "HZM"
var stu : HZMStudent?
}
class HZMStudent {
var age: Int = 18
var name: String = "HZM2"
var teacher: HZMTeacher?
}
func testCount() {
var t = HZMTeacher()
weak var t1 = t
print("end")
}
查看t
的引用計(jì)數(shù)變化
- 本質(zhì)上
t1 = t
并沒有增加引用計(jì)數(shù),但是t的地址存放的內(nèi)容卻發(fā)生了變化 - 弱引用聲明的變量是一個(gè)可選值,因?yàn)樵诔绦蜻\(yùn)行過(guò)程中是允許將當(dāng)前變量設(shè)置為
nil
的
在t1
處加斷點(diǎn),查看匯編
查看 源碼中的 swift_weakInit
函數(shù),這個(gè)函數(shù)是由WeakReference
來(lái)調(diào)用的,相當(dāng)于weak
字段在編譯器聲明過(guò)程中就自定義了一個(gè)WeakReference
的對(duì)象,其目的在于管理弱引用
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)
{
// 第一步、先拿到原本的引用計(jì)數(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
//第二步、創(chuàng)建sideTable
HeapObjectSideTableEntry *side = new HeapObjectSideTableEntry(getHeapObject());
//第三步、將創(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、先拿到原本的引用計(jì)數(shù)
2、創(chuàng)建sideTable
3、將創(chuàng)建的sideTable
地址給InlineRefCountBits
,并查看其初始化方法,根據(jù)sideTable
地址作了偏移操作并存儲(chǔ)到內(nèi)存,相當(dāng)于將sideTable
直接存儲(chǔ)到了64位的變量中
所以上面的0xc0000000200abb32
是HeapObjectSideTableEntry
實(shí)例對(duì)象的內(nèi)存地址,即散列表的地址
(除去63、62位)
第一反應(yīng)為啥要存它?我們繼續(xù)看
查看HeapObjectSideTableEntry
定義,其中有object
對(duì)象、refCounts
繼續(xù)往里進(jìn)
進(jìn)入SideTableRefCounts
,同InlineRefCounts
類似,實(shí)際做事的是SideTableRefCountBits
,繼承自RefCountBitsT
(存的是uint64_t類型的64位的信息),還有一個(gè)uint32_t
的weakBits
,即32位的位域信息
以0xc0000000200a45c4
為例,將62、63位清零,變成0x200A45C4
,然后左移3位(即InlineRefCountBits
初始化方法),變成0x100522E20
即HeapObjectSideTableEntry
對(duì)象地址,即散列表地址,然后通過(guò)x/8g
讀取(直接用編程計(jì)算器敲的)
發(fā)現(xiàn)最終結(jié)果的首地址就是我們當(dāng)前的實(shí)例對(duì)象
并且第二行是我們的強(qiáng)引用計(jì)數(shù)
,弱引用計(jì)數(shù)
總結(jié)一下
對(duì)于HeapObject
來(lái)說(shuō),其refCounts
有兩種:
- 無(wú)弱引用:
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位
}
}
循環(huán)引用
func test(){
var age = 10
let closure = {
age += 1
}
closure()
print(age)
}
test()
<!--打印結(jié)果--!>
11
從輸出結(jié)果中可以看出:閉包內(nèi)部對(duì)變量的修改將會(huì)改變外部原始變量的值
,主要原因是閉包會(huì)捕獲外部變量,這個(gè)與OC中的block是一致的
來(lái)看下面代碼
class HZMTeacher {
var age = 18
//反初始化器(當(dāng)前實(shí)例對(duì)象即將被回收)
deinit {
print("HZMTeacher deinit")
}
}
func test(){
var t = HZMTeacher()
}
test()
<!--打印結(jié)果--!>
HZMTeacher deinit
修改例子,通過(guò)閉包修改其屬性值
class HZMTeacher {
var age = 18
//反初始化器(當(dāng)前實(shí)例對(duì)象即將被回收)
deinit {
print("HZMTeacher deinit")
}
}
var t = HZMTeacher()
let clourse = {
t.age += 1
}
clourse()
<!--打印結(jié)果--!>
19
再次修改
class HZMTeacher {
var age = 18
//反初始化器(當(dāng)前實(shí)例對(duì)象即將被回收)
deinit {
print("HZMTeacher deinit")
}
}
func test(){
var t = HZMTeacher()
let clourse = {
t.age += 1
}
clourse()
}
<!--運(yùn)行結(jié)果-->
HZMTeacher deinit
根據(jù)運(yùn)行結(jié)果我們發(fā)現(xiàn),閉包對(duì) t
并沒有強(qiáng)引用
繼續(xù)修改
class HZMTeacher {
var age = 18
var completionBlock: (() ->())?
deinit {
print("HZMTeacher deinit")
}
}
func test(){
var t = HZMTeacher()
t.completionBlock = {
t.age += 1
}
}
test()
從運(yùn)行結(jié)果發(fā)現(xiàn),沒有執(zhí)行deinit
方法,即沒有打印HZMTeacher deinit
,所以這里有循環(huán)引用
循環(huán)引用的解決方法
Swift中有兩種解決方式
1.weak
使用weak
修飾閉包傳入的參數(shù),其中參數(shù)的類型是optional
func test(){
var t = HZMTeacher()
t.completionBlock = { [weak t] in
t?.age += 1
}
}
2.unowned(無(wú)主引用)
使用unowned
修飾閉包參數(shù),與weak
的區(qū)別在于unowned
不允許被設(shè)置為nil
,即總是假定有值
的,容易出現(xiàn)野指針情況。
func test(){
var t = HZMTeacher()
t.completionBlock = { [unowned t] in
t.age += 1
}
}
捕獲列表
[weak t] / [unowned t]
在swift中被稱為捕獲列表
1.定義在參數(shù)列表之前
2.[書寫方式]捕獲列表被寫成用逗號(hào)括起來(lái)的表達(dá)式列表,并用方括號(hào)括起來(lái)
3.如果使用捕獲列表,則即使省略參數(shù)名稱、參數(shù)類型和返回類型,也必須使用in關(guān)鍵字
4.[weak t] 就是取t的弱引用對(duì)象 類似weakself
我們看下面代碼
func test(){
var age = 0
var height = 0.0
//將變量age用來(lái)初始化捕獲列表中的常量age,即將0給了閉包中的age(值拷貝)
let clourse = {[age] in
print(age)
print(height)
}
age = 10
height = 1.85
clourse()
}
<!--打印結(jié)果--!>
0
1.85
所以從結(jié)果中可以得出:對(duì)于捕獲列表中的每個(gè)常量,閉包會(huì)利用周圍范圍內(nèi)具有相同名稱的常量/變量,來(lái)初始化捕獲列表中定義的常量。有以下幾點(diǎn)說(shuō)明:
捕獲列表中的常量是值拷貝,而不是引用
捕獲列表中的常量的相當(dāng)于復(fù)制了變量age的值
捕獲列表中的常量是只讀的,即不可修改
swift中Runtime探索
class HZMTeacher {
var age: Int = 18
func teach(){
print("teach")
}
}
let t = HZMTeacher()
func test(){
var methodCount: UInt32 = 0
let methodList = class_copyMethodList(HZMTeacher.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(HZMTeacher.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é)果--!>
test run
結(jié)果發(fā)現(xiàn)并沒有打印方法和屬性
- 給屬性 和 方法 都加上
@objc
,可以打印嗎?
Runtime01.png
從運(yùn)行結(jié)果看,是可以打印,但是由于類并沒有暴露給OC,所以O(shè)C是無(wú)法使用的,這樣做是沒有意義的
- 如果
swift
的類繼承NSObject
,沒有@objc
修飾屬性和方法,是否可以打印全部屬性+方法?
Runtime02.png
從結(jié)果發(fā)現(xiàn)獲取的只有init
方法,主要是因?yàn)樵?swift.h
文件中暴露出來(lái)的只有init
方法
如果想讓OC能使用,必須類繼承NSObject + @objc修飾屬性、方法
- 如果去掉
@objc
修飾屬性,將方法改成dynamic
修飾,是否可以打印方法?
Runtime04.png
從結(jié)果可以看出,依舊不能被OC獲取到,需要修改為@objc dynamic
修飾
結(jié)論:
對(duì)于純swift類
來(lái)說(shuō),沒有
動(dòng)態(tài)特性dynamic
(因?yàn)閟wift是靜態(tài)語(yǔ)言),方法和屬性不加任何修飾符的情況下,已經(jīng)不具備runtime
特性,此時(shí)的方法調(diào)度,依舊是函數(shù)表調(diào)度即V_Table調(diào)度
對(duì)于純swift類
,方法和屬性添加@objc
標(biāo)識(shí)的情況下,可以通過(guò)runtime API獲取
到,但是在OC
中是無(wú)法進(jìn)行調(diào)度
的,原因是因?yàn)?code>swift.h文件中沒有swift類的聲明
對(duì)于繼承自NSObject類
來(lái)說(shuō),如果想要?jiǎng)討B(tài)的獲取當(dāng)前屬性+方法
,必須在其聲明前添加 @objc
關(guān)鍵字,如果想要使用方法交換
,還必須在屬性+方法前添加dynamic關(guān)鍵字
,否則當(dāng)前屬性+方法只是暴露給OC使用,而不具備任何動(dòng)態(tài)特性
objc源碼驗(yàn)證(由于xcode12.2暫時(shí)無(wú)法運(yùn)行objc源碼,下列驗(yàn)證圖片僅供參考)
- 進(jìn)入
class_copyMethodList
源碼,斷住,查看此時(shí)的cls
,其中data()
存儲(chǔ)類的信息
進(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;
}
在之前的文章中有提到,其中TargetAnyClassMetadata
繼承自TargetHeapMetaData
,其中只有一個(gè)屬性kind
,TargetAnyClassMetadata
有四個(gè)屬性:isa、superclass、cacheData、data即bits
所以swift為了保留和OC交互,其在底層存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu)上和OC是一致的
objc源碼
中搜索swift_class_t
,繼承自objc_class
,保留了OC模板類的4個(gè)屬性,其次才是自己的屬性
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?
必須通過(guò)NSObject聲明,來(lái)幫助編譯器判斷,當(dāng)前類是一個(gè)和OC交互的類
元類型、AnyClass、Self
AnyObject
AnyObject
:代表任意類的instance、類的類型、僅類遵守的協(xié)議
class HZMTeacher: NSObject {
var age: Int = 18
}
var t = HZMTeacher()
//此時(shí)代表的就是當(dāng)前HZMTeacher的實(shí)例對(duì)象
var t1: AnyObject = t
//此時(shí)代表的是HZMTeacher這個(gè)類的類型
var t2: AnyObject = HZMTeacher
//繼承自AnyObject,表示JSONMap協(xié)議只有類才可以遵守
protocol JSONMap: AnyObject { }
如果是結(jié)構(gòu)體遵守協(xié)議,會(huì)報(bào)錯(cuò),表示JSONMap協(xié)議只有類才可以遵守
Any
Any
:代表任意類型,包括 function類型 或者Optional類型
,可以理解為AnyObject是Any的子集
//如果使用AnyObject會(huì)報(bào)錯(cuò),而Any不會(huì)
var array: [Any] = [1, "HZM", "", true]
AnyClass
AnyClass
:代表任意類的實(shí)例的類型 ,類型是AnyObject.Type
查看定義,是public typealias AnyClass = AnyObject.Type
T.self & T.Type
T.self
:如果T是實(shí)例對(duì)象,返回的就是它本身,如果T是類,那么返回的是MetaData
T.Type
:一種類型
T.self
是 T.Type
類型
簡(jiǎn)單看一個(gè)例子
//此時(shí)的self類型是 HZMTeacher.Type
var t = HZMTeacher.self
再看下下面的例子:
var t = HZMTeacher()
//實(shí)例對(duì)象地址:實(shí)例對(duì)象.self 返回實(shí)例對(duì)象本身
var t1 = t.self
//存儲(chǔ)metadata元類型
var t2 = HZMTeacher.self
type(of:)
type(of:):
用來(lái)獲取一個(gè)值的動(dòng)態(tài)類型
var age = 10 as NSNumber
print(type(of: age))
<!--打印結(jié)果--!>
__NSCFNumber
//value - static type 靜態(tài)類型:編譯時(shí)期確定好的
//type(of:) - dynamic type:Int
var age = 10
//value的靜態(tài)類型就是Any
func test(_ value: Any){
print(type(of: value))
}
test(age)
<!--打印結(jié)果--!>
Int
實(shí)踐一下下
class HZMTeacher{
var age = 18
var double = 1.85
func teach(){
print("HZMTeacher teach")
}
}
class HZMPartTimeTeacher: HZMTeacher {
override func teach() {
print("HZMPartTimeTeacher teach")
}
}
func test(_ value: HZMTeacher){
let valueType = type(of: value)
value.teach()
print(value)
}
var t = HZMPartTimeTeacher()
test(t)
<!--打印結(jié)果--!>
HZMPartTimeTeacher teach
HZMTest.HZMPartTimeTeacher
運(yùn)行時(shí)value
的類型還是HZMPartTimeTeacher
,只是傳參的時(shí)候告訴編譯器是HZMTeacher
類型
protocol TestProtocol {
}
class HZMTeacher: TestProtocol{
var age = 18
var double = 1.85
func teach(){
print("HZMTeacher teach")
}
}
func test(_ value: TestProtocol){
let valueType = type(of: value)
print(valueType)
}
var t = HZMTeacher()
let t1: TestProtocol = HZMTeacher()
test(t)
test(t1)
<!--打印結(jié)果--!>
HZMTeacher
HZMTeacher
3.如果將test中參數(shù)的類型修改為泛型,此時(shí)的打印是什么?
func test<T>(_ value: T){
let valueType = type(of: value)
print(valueType)
}
<!--打印結(jié)果--!>
HZMTeacher
TestProtocol
從結(jié)果中發(fā)現(xiàn),打印并不一致,原因是因?yàn)楫?dāng)有協(xié)議、泛型時(shí)
,當(dāng)前的編譯器并不能推斷出準(zhǔn)確的類型,需要將value轉(zhuǎn)換為Any
,修改后的代碼如下:
func test<T>(_ value: T){
let valueType = type(of: value as Any)
print(valueType)
}
總結(jié)
當(dāng)無(wú)弱引用時(shí),HeapObject
中的refCounts
等于 strongCount
+ unownedCount
當(dāng)有弱引用時(shí),HeapObject
中的refCounts
等于 object
+ xxx
+ (strongCount + unownedCount)
+ weakCount
循環(huán)應(yīng)用可以通過(guò)weak / unowned
修飾參數(shù)來(lái)解決
swift中閉包的捕獲列表
是值拷貝,即深拷貝
,是一個(gè)只讀的常量
swift由于是靜態(tài)語(yǔ)言
,所以屬性、方法
在不加任何修飾符的情況下時(shí)是不具備動(dòng)態(tài)性即Runtime特性
的,此時(shí)的方法調(diào)度是V-Table函數(shù)表調(diào)度
如果想要OC
使用swift類中的方法、屬性
,需要class繼承NSObject,并使用@objc修飾
如果想要使用方法交換
,除了繼承NSObject+@objc修飾
,還必須使用dynamic修飾
Any:任意類型,包括function
類型、optional
類型
AnyObject:任意類的instance
、類的類型、僅類遵守的協(xié)議,可以看作是Any的子類
AnyClass:任意實(shí)例類型,類型是AnyObject.Type
T.self:如果T是實(shí)例對(duì)象,則表示它本身,如果是類,則表示metadata.T.self
的類型是T.Type