iOS內(nèi)存管理引用計(jì)數(shù)示例分析
內(nèi)存管理機(jī)制
目前流行的內(nèi)存管理機(jī)制主要有GC
和RC
兩種。
GC
(Garbage Collection):垃圾回收機(jī)制,定期查找不再使用的對(duì)象,釋放對(duì)象占用的內(nèi)存。RC
(Reference Counting):引用計(jì)數(shù)機(jī)制。采用引用計(jì)數(shù)來(lái)管理對(duì)象的內(nèi)存,當(dāng)需要持有一個(gè)對(duì)象時(shí),使它的引用計(jì)數(shù) +1;當(dāng)不需要持有一個(gè)對(duì)象的時(shí)候,使它的引用計(jì)數(shù) -1;當(dāng)一個(gè)對(duì)象的引用計(jì)數(shù)為 0,該對(duì)象就會(huì)被銷(xiāo)毀。
Objective-C
支持三種內(nèi)存管理機(jī)制:ARC
、MRC
和GC
,但Objective-C
的GC
機(jī)制有平臺(tái)局限性,僅限于MacOS
開(kāi)發(fā)中,iOS
開(kāi)發(fā)用的是RC
機(jī)制,從MRC
到現(xiàn)在的ARC
。
一個(gè)新創(chuàng)建的OC對(duì)象引用計(jì)數(shù)默認(rèn)是1,當(dāng)引用計(jì)數(shù)減為0,OC對(duì)象就會(huì)銷(xiāo)毀,釋放其占用的內(nèi)存空間
調(diào)用retain
會(huì)讓OC對(duì)象的引用計(jì)數(shù)+1,調(diào)用release
會(huì)讓OC對(duì)象的引用計(jì)數(shù)-1
內(nèi)存管理的經(jīng)驗(yàn)總結(jié)
- 當(dāng)調(diào)用
alloc
、new
、copy
、mutableCopy
方法返回了一個(gè)對(duì)象,在不需要這個(gè)對(duì)象時(shí),要調(diào)用release
或者autorelease
來(lái)釋放它 - 想擁有某個(gè)對(duì)象,就讓它的引用計(jì)數(shù)+1;不想再擁有某個(gè)對(duì)象,就讓它的引用計(jì)數(shù)-1
- 可以通過(guò)以下私有函數(shù)來(lái)查看自動(dòng)釋放池的情況
extern void _objc_autoreleasePoolPrint(void);
以上我們對(duì) “引用計(jì)數(shù)” 這一概念做了初步了解,Objective-C 中的 “對(duì)象” 通過(guò)引用計(jì)數(shù)功能來(lái)管理它的內(nèi)存生命周期。那么,對(duì)象的引用計(jì)數(shù)是如何存儲(chǔ)的呢?它存儲(chǔ)在哪個(gè)數(shù)據(jù)結(jié)構(gòu)里?
首先,不得不提一下isa
。
isa
isa
指針用來(lái)維護(hù) “對(duì)象” 和 “類(lèi)” 之間的關(guān)系,并確保對(duì)象和類(lèi)能夠通過(guò)isa
指針找到對(duì)應(yīng)的方法、實(shí)例變量、屬性、協(xié)議等;- 在 arm64 架構(gòu)之前,
isa
就是一個(gè)普通的指針,直接指向objc_class
,存儲(chǔ)著Class
、Meta-Class
對(duì)象的內(nèi)存地址。instance
對(duì)象的isa
指向class
對(duì)象,class
對(duì)象的isa
指向meta-class
對(duì)象; - 從 arm64 架構(gòu)開(kāi)始,對(duì)
isa
進(jìn)行了優(yōu)化,用nonpointer
表示,變成了一個(gè)共用體(union
)結(jié)構(gòu),還使用位域來(lái)存儲(chǔ)更多的信息。將 64 位的內(nèi)存數(shù)據(jù)分開(kāi)來(lái)存儲(chǔ)著很多的東西,其中的 33 位才是拿來(lái)存儲(chǔ)class
、meta-class
對(duì)象的內(nèi)存地址信息。要通過(guò)位運(yùn)算將isa
的值& ISA_MASK
掩碼,才能得到class
、meta-class
對(duì)象的內(nèi)存地址。
// objc.h struct objc_object { Class isa; // 在 arm64 架構(gòu)之前 }; // objc-private.h struct objc_object { private: isa_t isa; // 在 arm64 架構(gòu)開(kāi)始 }; union isa_t { isa_t() { } isa_t(uintptr_t value) : bits(value) { } Class cls; uintptr_t bits; #if SUPPORT_PACKED_ISA // extra_rc must be the MSB-most field (so it matches carry/overflow flags) // nonpointer must be the LSB (fixme or get rid of it) // shiftcls must occupy the same bits that a real class pointer would // bits + RC_ONE is equivalent to extra_rc + 1 // RC_HALF is the high bit of extra_rc (i.e. half of its range) // future expansion: // uintptr_t fast_rr : 1; // no r/r overrides // uintptr_t lock : 2; // lock for atomic property, @synch // uintptr_t extraBytes : 1; // allocated with extra bytes # if __arm64__ // 在 __arm64__ 架構(gòu)下 # define ISA_MASK 0x0000000ffffffff8ULL // 用來(lái)取出 Class、Meta-Class 對(duì)象的內(nèi)存地址 # define ISA_MAGIC_MASK 0x000003f000000001ULL # define ISA_MAGIC_VALUE 0x000001a000000001ULL struct { uintptr_t nonpointer : 1; // 0:代表普通的指針,存儲(chǔ)著 Class、Meta-Class 對(duì)象的內(nèi)存地址 // 1:代表優(yōu)化過(guò),使用位域存儲(chǔ)更多的信息 uintptr_t has_assoc : 1; // 是否有設(shè)置過(guò)關(guān)聯(lián)對(duì)象,如果沒(méi)有,釋放時(shí)會(huì)更快 uintptr_t has_cxx_dtor : 1; // 是否有C++的析構(gòu)函數(shù)(.cxx_destruct),如果沒(méi)有,釋放時(shí)會(huì)更快 uintptr_t shiftcls : 33; // 存儲(chǔ)著 Class、Meta-Class 對(duì)象的內(nèi)存地址信息 uintptr_t magic : 6; // 用于在調(diào)試時(shí)分辨對(duì)象是否未完成初始化 uintptr_t weakly_referenced : 1; // 是否有被弱引用指向過(guò),如果沒(méi)有,釋放時(shí)會(huì)更快 uintptr_t deallocating : 1; // 對(duì)象是否正在釋放 uintptr_t has_sidetable_rc : 1; // 如果為1,代表引用計(jì)數(shù)過(guò)大無(wú)法存儲(chǔ)在 isa 中,那么超出的引用計(jì)數(shù)會(huì)存儲(chǔ)在一個(gè)叫 SideTable 結(jié)構(gòu)體的 RefCountMap(引用計(jì)數(shù)表)散列表中 uintptr_t extra_rc : 19; // 里面存儲(chǔ)的值是對(duì)象本身之外的引用計(jì)數(shù)的數(shù)量,retainCount - 1 # define RC_ONE (1ULL<<45) # define RC_HALF (1ULL<<18) }; ...... // 在 __x86_64__ 架構(gòu)下 };
如果isa
非nonpointer
,即 arm64 架構(gòu)之前的isa
指針。由于它只是一個(gè)普通的指針,存儲(chǔ)著Class
、Meta-Class
對(duì)象的內(nèi)存地址,所以它本身不能存儲(chǔ)引用計(jì)數(shù),所以以前對(duì)象的引用計(jì)數(shù)都存儲(chǔ)在一個(gè)叫SideTable
結(jié)構(gòu)體的RefCountMap
(引用計(jì)數(shù)表)散列表中。
如果isa
是nonpointer
,則它本身可以存儲(chǔ)一些引用計(jì)數(shù)。從以上union isa_t
的定義中我們可以得知,isa_t
中存儲(chǔ)了兩個(gè)引用計(jì)數(shù)相關(guān)的東西:extra_rc
和has_sidetable_rc
。
- extra_rc:里面存儲(chǔ)的值是對(duì)象本身之外的引用計(jì)數(shù)的數(shù)量,這 19 位如果不夠存儲(chǔ),
has_sidetable_rc
的值就會(huì)變?yōu)?1; - has_sidetable_rc:如果為 1,代表引用計(jì)數(shù)過(guò)大無(wú)法存儲(chǔ)在
isa
中,那么超出的引用計(jì)數(shù)會(huì)存儲(chǔ)SideTable
的RefCountMap
中。
所以,如果isa
是nonpointer
,則對(duì)象的引用計(jì)數(shù)存儲(chǔ)在它的isa_t
的extra_rc
中以及SideTable
的RefCountMap
中。
SideTable
// NSObject.mm struct SideTable { spinlock_t slock; // 自旋鎖 RefcountMap refcnts; // 引用計(jì)數(shù)表(散列表) weak_table_t weak_table; // 弱引用表(散列表) ...... }
SideTable
存儲(chǔ)在SideTables()
中,SideTables()
本質(zhì)也是一個(gè)散列表,可以通過(guò)對(duì)象指針來(lái)獲取它對(duì)應(yīng)的(引用計(jì)數(shù)表或者弱引用表)在哪一個(gè)SideTable
中。在非嵌入式系統(tǒng)下,SideTables()
中有 64 個(gè)SideTable
。以下是SideTables()
的定義:
// NSObject.mm static objc::ExplicitInit<StripedMap<SideTable>> SideTablesMap; static StripedMap<SideTable>& SideTables() { return SideTablesMap.get(); }
所以,查找對(duì)象的引用計(jì)數(shù)表需要經(jīng)過(guò)兩次哈希查找:
- ① 第一次根據(jù)當(dāng)前對(duì)象的內(nèi)存地址,經(jīng)過(guò)哈希查找從
SideTables()
中取出它所在的SideTable
; - ② 第二次根據(jù)當(dāng)前對(duì)象的內(nèi)存地址,經(jīng)過(guò)哈希查找從
SideTable
中的refcnts
中取出它的引用計(jì)數(shù)表。
使用多個(gè)SideTable
+分離鎖技術(shù)方案是為了保證線程安全的同時(shí)兼顧訪問(wèn)效率
以上就是iOS內(nèi)存管理引用計(jì)數(shù)示例分析的詳細(xì)內(nèi)容,更多關(guān)于iOS內(nèi)存管理引用計(jì)數(shù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
iOS評(píng)分(評(píng)價(jià))星星圖打分功能
這篇文章主要介紹了iOS評(píng)分(評(píng)價(jià))星星圖打分功能,評(píng)分視圖分為展示和評(píng)分兩種,具體詳情大家可以通過(guò)本文詳細(xì)學(xué)習(xí)2016-11-11使用IOS AirPrint實(shí)現(xiàn)打印功能詳解
這篇文章主要介紹了使用IOS AirPrint實(shí)現(xiàn)打印功能詳解,想了解無(wú)線打印的同學(xué),一定要看一下2021-04-04IOS 聊天界面(自適應(yīng)文字)的實(shí)現(xiàn)
本文主要介紹一個(gè)實(shí)現(xiàn)聊天界面的思路過(guò)程,具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-03-03iOS實(shí)現(xiàn)無(wú)限循環(huán)滾動(dòng)的TableView實(shí)戰(zhàn)教程
這篇文章主要給大家介紹了關(guān)于iOS實(shí)現(xiàn)無(wú)限循環(huán)滾動(dòng)的TableView的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-05-05iOS基于UITableView實(shí)現(xiàn)多層展開(kāi)與收起
這篇文章主要為大家詳細(xì)介紹了iOS基于UITableView實(shí)現(xiàn)多層展開(kāi)與收起的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03iOS App設(shè)計(jì)模式開(kāi)發(fā)中對(duì)建造者模式的運(yùn)用實(shí)例
這篇文章主要介紹了iOS App設(shè)計(jì)模式開(kāi)發(fā)中對(duì)建造者模式的運(yùn)用實(shí)例,示例代碼為傳統(tǒng)的Objective-C,需要的朋友可以參考下2016-04-04iOS開(kāi)發(fā)中AVPlayer的簡(jiǎn)單應(yīng)用
這篇文章主要介紹了iOS開(kāi)發(fā)中AVPlayer的簡(jiǎn)單應(yīng)用,文中給出了簡(jiǎn)單的介紹和示例代碼,相信對(duì)大家學(xué)習(xí)AVPlayer的應(yīng)用具有一定的參考價(jià)值,需要的朋友們下面跟著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)。2017-02-02iOS實(shí)現(xiàn)選項(xiàng)卡效果的方法
選項(xiàng)卡在我們?nèi)粘i_(kāi)發(fā)的時(shí)候經(jīng)常要用到,所以這篇文章給大家分享一種iOS實(shí)現(xiàn)的簡(jiǎn)單選項(xiàng)卡效果,很適合大家學(xué)習(xí)和使用,有需要的可以參考借鑒,下面來(lái)一起看看吧。2016-09-09