和OC
一樣踩娘,Swift
中也是通過引用計(jì)數(shù)
的方式來管理對象的內(nèi)存的橘沥。在Swift類 結(jié)構(gòu)探究中毅戈,分析過引用計(jì)數(shù)refCounts
,它是RefCounts
類型骡尽,class類型
遣妥,占8字節(jié)
大小。
一攀细、強(qiáng)引用
class Animal {
var age: Int = 10
var name: String = "dog"
}
var t = Animal()
var t1 = t
var t2 = t
斷點(diǎn)箫踩,查看t
的的內(nèi)存,refCounts
是0x0000000600000003
:
1.1
從HeapObject
開始分析引用計(jì)數(shù)的真正表示形態(tài)谭贪,源碼 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
屬性取了一個別名套媚,所以bits
的真正類型是uint64_t
缚态,即64位整型數(shù)組
。
1.2 對象創(chuàng)建
然后堤瘤,繼續(xù)分析swift
中對象創(chuàng)建的底層方法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
方法中桥帆,干事的是RefCountBits
:
enum Initialized_t { Initialized };
//對應(yīng)的RefCounts方法
// Refcount of a new object is 1.
constexpr RefCounts(Initialized_t)
: refCounts(RefCountBits(0, 1)) {}
進(jìn)入RefCountBits
定義医增,也是一個模板定義
:
template <typename RefCountBits>
class RefCounts {
std::atomic<RefCountBits> refCounts;
...
}
重點(diǎn):最終,真正的初始化地方是下面這個老虫,實(shí)際上是做了一個位域操作
叶骨,根據(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))
{ }
referCounts
的本質(zhì)是RefCountBitsInt
中的type屬性
,而RefCountBitsInt
又是模板類RefCountBitsT
的模板類型T
祈匙,所以RefCountBits(0, 1)
實(shí)質(zhì)調(diào)用的是模板類RefCountBitsT
的構(gòu)造方法
忽刽,strongExtraCount
傳值為0
,unownedCount
傳值為1
菊卷。
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
。至此洁闰,我們分析一下上面的引用計(jì)數(shù)值0x0000000600000003
,用二進(jìn)制展示:
如圖万细,
33位置
開始的強(qiáng)引用計(jì)數(shù)StrongExtraRefCount
為0011
扑眉,轉(zhuǎn)換成十進(jìn)制
就是3
。下面通過
sil
驗(yàn)證一下:關(guān)于
copy_addr
赖钞,sil
文檔有解釋腰素,其實(shí)現(xiàn)是對object的引用計(jì)數(shù)作+1
操作。有興趣的可以自己去查一下雪营。需要注意的是:
var t = Animal()
此時已經(jīng)有了引用計(jì)數(shù)
弓千,即,OC中創(chuàng)建實(shí)例對象時為0献起;Swift中創(chuàng)建實(shí)例對象時默認(rèn)為1洋访。可以通過
CFGetRetainCount
獲取引用計(jì)數(shù):二、弱引用
class Animal {
var age: Int = 10
var name: String = "cat"
var dog: Dog?
}
class Dog {
var age = 20
var animal: Animal?
}
func test(){
var t = Animal()
weak var t1 = t
print("end")
}
test()
查看 t
的引用計(jì)數(shù):
弱引用聲明
的變量
是一個可選值
谴餐,因?yàn)樵诔绦蜻\(yùn)行過程中是允許將當(dāng)前變量設(shè)置為nil
的姻政。
在t1
處加斷點(diǎn),查看匯編岂嗓,發(fā)現(xiàn):swift_weakInit
汁展。查看源碼,這個函數(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、先拿到原本的引用計(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
//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;
}
總結(jié):
- 先拿到原本的引用計(jì)數(shù)树姨;
- 創(chuàng)建
sideTable
;- 將創(chuàng)建的
sideTable
地址給InlineRefCountBits
桥状,并查看其初始化方法帽揪,根據(jù)sideTable
地址作了偏移
操作并存儲
到內(nèi)存,相當(dāng)于將sideTable直接存儲到了64位的變量
中辅斟。
通過上面的底層流程分析转晰,我們可以get到關(guān)鍵的2點(diǎn):
一 通過HeapObjectSideTableEntry
初始化散列表
class HeapObjectSideTableEntry {
// FIXME: does object need to be atomic?
std::atomic<HeapObject*> object;
SideTableRefCounts refCounts;
...
}
上述源碼中可知,弱引用對象對應(yīng)的引用計(jì)數(shù)refCounts
是SideTableRefCounts
類型士飒,而強(qiáng)引用對象的是InlineRefCounts
類型查邢。
接下來我們看看SideTableRefCounts
:
typedef RefCounts<SideTableRefCountBits> SideTableRefCounts;
繼續(xù)搜索SideTableRefCountBits
里面包含了成員
uint32_t weakBits
,即一個32位域
的信息酵幕。
二 通過InlineRefCountBits
初始化散列表的數(shù)據(jù)
LLVM_ATTRIBUTE_ALWAYS_INLINE
RefCountBitsT(HeapObjectSideTableEntry* side)
: bits((reinterpret_cast<BitsType>(side) >> Offsets::SideTableUnusedLowBits)
| (BitsType(1) << Offsets::UseSlowRCShift)
| (BitsType(1) << Offsets::SideTableMarkShift))
{
assert(refcountIsInline);
}
這里繼承的bits
構(gòu)造方法扰藕,而bits
定義
BitsType bits;
typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::Type
BitsType;
和強(qiáng)引用
一樣,來到了RefCountBitsInt
芳撒,這個之前分析過邓深,就是uint64_t
類型,存的是64位域
信息笔刹。
綜合1
和 2
兩點(diǎn)的論述可得出:64位
用于記錄 原有引用計(jì)數(shù)
芥备;32位
用于記錄 弱引用計(jì)數(shù)
。
為何t
的引用計(jì)數(shù)是:0xc0000000200e08ba
舌菜?
上述分析中我們知道萌壳,在 InlineRefCountBits
初始化散列表的數(shù)據(jù)時,執(zhí)行了(reinterpret_cast<BitsType>(side) >> Offsets::SideTableUnusedLowBits
這句代碼日月,而
static const size_t SideTableUnusedLowBits = 3;
對side右移了3位
袱瓮,所以此時,將0xc0000000200e08ba 左移3位
是0x1007045D0
山孔,就是散列表的地址
懂讯。再x/8g
查看:
三、循環(huán)引用
var age = 10
let clourse = {
age += 1
}
clourse()
print(age)
//打印結(jié)果
11
從輸出結(jié)果中可以看出:閉包內(nèi)部對變量的修改會改變外部原始變量的值台颠,原因是閉包會捕獲外部變量
褐望,這個與OC中的block
是一致
的。
deinit
class Animal {
var age = 10
deinit {
print("Animal deinit")
}
}
func test(){
var t = Animal()
let clourse = {
t.age += 10
}
clourse()
print(t.age)
}
test()
//打印結(jié)果
//Animal deinit
可見串前,deinit
是在當(dāng)前實(shí)例對象即將被回收時
觸發(fā)瘫里。
接下來,我們把age
放到類中荡碾,閉包中再去修改時會怎樣:
class Animal {
var age = 10
deinit {
print("Animal deinit")
}
}
var t = Animal()
let clourse = {
t.age += 10
}
clourse()
print(t.age)
//打印結(jié)果
//20
//Animal deinit
在Animal類
增加閉包屬性
:
class Animal {
var age = 10
var completionBlock: (() ->())?
deinit {
print("Animal deinit")
}
}
func test(){
var t = Animal()
t.completionBlock = {
t.age += 10
}
print(t.age)
}
test()
//打印結(jié)果
//10
從運(yùn)行結(jié)果發(fā)現(xiàn)谨读,t.age
還是1
,并且沒有執(zhí)行deinit
方法坛吁,這里存在循環(huán)引用
劳殖。
循環(huán)引用的處理
1 weak修飾
閉包傳入的參數(shù)铐尚。weak修飾
后的變量是optional類型
,所以t?.age += 1
哆姻。
func test(){
var t = Animal()
t.completionBlock = { [weak t] in
t?.age += 10
}
print(t.age)
}
2 unowned修飾
閉包參數(shù)
func test(){
var t = Animal()
t.completionBlock = { [unowned t] in
t.age += 10
}
print(t.age)
}
[weak t]
或 [unowned t]
宣增,稱為捕獲列表
。定義在參數(shù)列表之前矛缨,如果使用捕獲列表爹脾,那么即使省略參數(shù)名稱、參數(shù)類型和返回類型箕昭,也必須使用in關(guān)鍵字
灵妨。
捕獲列表的值
func test(){
var age = 1
var height = 1.0
let clourse = {[age] in
print(age)
print(height)
}
age = 10
height = 1.85
clourse()
}
test()
//打印結(jié)果
//1
//1.85
如上,age
值改變了落竹,height
未被捕獲泌霍,值未變。
捕獲列表特點(diǎn): 捕獲列表中的常量是
值拷貝
述召,而不是引用拷貝烹吵,因此,它是只讀
的桨武,即不可修改
。
Runtime
Swift
是一門靜態(tài)語言
锈津,本身不具備動態(tài)性呀酸,不像OC
有Runtime
運(yùn)行時的機(jī)制(此處指OC提供運(yùn)行時API供程序員操作)。但由于Swift兼容OC
琼梆,所以可以轉(zhuǎn)成OC類和函數(shù)性誉,利用OC的運(yùn)行時機(jī)制,來實(shí)現(xiàn)動態(tài)性
茎杂。
class Animal {
var age: Int = 18
func eat(){
print("eat")
}
}
let t = Animal()
func test(){
var methodCount: UInt32 = 0
let methodList = class_copyMethodList(Animal.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(Animal.self, &count)
for i in 0..<numericCast(count) {
if let property = proList?[i]{
let propertyName = String(utf8String: property_getName(property))
print("------成員屬性名稱:\(propertyName!)")
}else{
print("沒有找到你要的屬性")
}
}
print("test run")
}
test()
嘗試:
1错览、以上代碼,沒有打印
煌往。
2倾哺、給方法和屬性添加@objc
修飾,可以打印
刽脖。
3羞海、類Animal繼承NSObject
,不用@objc
修飾曲管。只打印了初始化方法
却邓,因?yàn)樵趕wift.h文件中暴露出來的只有init方法。
注意
:如果要讓OC調(diào)用
院水,那么必須 繼承NSObject + @objc修飾
腊徙。
4简十、去掉@objc修飾,改成dynamic修飾 + NSObject
撬腾,同3螟蝙。
5、@objc修飾 + dynamic修飾 + NSObject
时鸵。
關(guān)于方法調(diào)用胶逢,參考Swift方法調(diào)用
補(bǔ)充
AnyObject
:代表任意類的instance,類的類型饰潜,類遵守的協(xié)議初坠,但struct?不行。
Any
:代表任意類型
彭雾,包括funcation類型或者Optional類型碟刺。
AnyClass
:代表任意實(shí)例的類型。
T.self
:如果T
為實(shí)例對象薯酝,返回的就是它本身
半沽;T
是類,返回的是Metadata
吴菠。
type(of:)
:用于獲取一個值的動態(tài)類型者填,編譯期
時,value
的類型是Any類型
做葵;運(yùn)行期
時占哟,type(of:)
獲取的是真實(shí)類型
。