iOS內存管理引用計數(shù)示例分析
內存管理機制
目前流行的內存管理機制主要有GC和RC兩種。
GC(Garbage Collection):垃圾回收機制,定期查找不再使用的對象,釋放對象占用的內存。RC(Reference Counting):引用計數(shù)機制。采用引用計數(shù)來管理對象的內存,當需要持有一個對象時,使它的引用計數(shù) +1;當不需要持有一個對象的時候,使它的引用計數(shù) -1;當一個對象的引用計數(shù)為 0,該對象就會被銷毀。
Objective-C支持三種內存管理機制:ARC、MRC和GC,但Objective-C的GC機制有平臺局限性,僅限于MacOS開發(fā)中,iOS開發(fā)用的是RC機制,從MRC到現(xiàn)在的ARC。
一個新創(chuàng)建的OC對象引用計數(shù)默認是1,當引用計數(shù)減為0,OC對象就會銷毀,釋放其占用的內存空間
調用retain會讓OC對象的引用計數(shù)+1,調用release會讓OC對象的引用計數(shù)-1
內存管理的經驗總結
- 當調用
alloc、new、copy、mutableCopy方法返回了一個對象,在不需要這個對象時,要調用release或者autorelease來釋放它 - 想擁有某個對象,就讓它的引用計數(shù)+1;不想再擁有某個對象,就讓它的引用計數(shù)-1
- 可以通過以下私有函數(shù)來查看自動釋放池的情況
extern void _objc_autoreleasePoolPrint(void);
以上我們對 “引用計數(shù)” 這一概念做了初步了解,Objective-C 中的 “對象” 通過引用計數(shù)功能來管理它的內存生命周期。那么,對象的引用計數(shù)是如何存儲的呢?它存儲在哪個數(shù)據(jù)結構里?
首先,不得不提一下isa。
isa
isa指針用來維護 “對象” 和 “類” 之間的關系,并確保對象和類能夠通過isa指針找到對應的方法、實例變量、屬性、協(xié)議等;- 在 arm64 架構之前,
isa就是一個普通的指針,直接指向objc_class,存儲著Class、Meta-Class對象的內存地址。instance對象的isa指向class對象,class對象的isa指向meta-class對象; - 從 arm64 架構開始,對
isa進行了優(yōu)化,用nonpointer表示,變成了一個共用體(union)結構,還使用位域來存儲更多的信息。將 64 位的內存數(shù)據(jù)分開來存儲著很多的東西,其中的 33 位才是拿來存儲class、meta-class對象的內存地址信息。要通過位運算將isa的值& ISA_MASK掩碼,才能得到class、meta-class對象的內存地址。
// objc.h
struct objc_object {
Class isa; // 在 arm64 架構之前
};
// objc-private.h
struct objc_object {
private:
isa_t isa; // 在 arm64 架構開始
};
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__ 架構下
# define ISA_MASK 0x0000000ffffffff8ULL // 用來取出 Class、Meta-Class 對象的內存地址
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
uintptr_t nonpointer : 1; // 0:代表普通的指針,存儲著 Class、Meta-Class 對象的內存地址
// 1:代表優(yōu)化過,使用位域存儲更多的信息
uintptr_t has_assoc : 1; // 是否有設置過關聯(lián)對象,如果沒有,釋放時會更快
uintptr_t has_cxx_dtor : 1; // 是否有C++的析構函數(shù)(.cxx_destruct),如果沒有,釋放時會更快
uintptr_t shiftcls : 33; // 存儲著 Class、Meta-Class 對象的內存地址信息
uintptr_t magic : 6; // 用于在調試時分辨對象是否未完成初始化
uintptr_t weakly_referenced : 1; // 是否有被弱引用指向過,如果沒有,釋放時會更快
uintptr_t deallocating : 1; // 對象是否正在釋放
uintptr_t has_sidetable_rc : 1; // 如果為1,代表引用計數(shù)過大無法存儲在 isa 中,那么超出的引用計數(shù)會存儲在一個叫 SideTable 結構體的 RefCountMap(引用計數(shù)表)散列表中
uintptr_t extra_rc : 19; // 里面存儲的值是對象本身之外的引用計數(shù)的數(shù)量,retainCount - 1
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
...... // 在 __x86_64__ 架構下
};
如果isa非nonpointer,即 arm64 架構之前的isa指針。由于它只是一個普通的指針,存儲著Class、Meta-Class對象的內存地址,所以它本身不能存儲引用計數(shù),所以以前對象的引用計數(shù)都存儲在一個叫SideTable結構體的RefCountMap(引用計數(shù)表)散列表中。
如果isa是nonpointer,則它本身可以存儲一些引用計數(shù)。從以上union isa_t的定義中我們可以得知,isa_t中存儲了兩個引用計數(shù)相關的東西:extra_rc和has_sidetable_rc。
- extra_rc:里面存儲的值是對象本身之外的引用計數(shù)的數(shù)量,這 19 位如果不夠存儲,
has_sidetable_rc的值就會變?yōu)?1; - has_sidetable_rc:如果為 1,代表引用計數(shù)過大無法存儲在
isa中,那么超出的引用計數(shù)會存儲SideTable的RefCountMap中。
所以,如果isa是nonpointer,則對象的引用計數(shù)存儲在它的isa_t的extra_rc中以及SideTable的RefCountMap中。
SideTable
// NSObject.mm
struct SideTable {
spinlock_t slock; // 自旋鎖
RefcountMap refcnts; // 引用計數(shù)表(散列表)
weak_table_t weak_table; // 弱引用表(散列表)
......
}
SideTable存儲在SideTables()中,SideTables()本質也是一個散列表,可以通過對象指針來獲取它對應的(引用計數(shù)表或者弱引用表)在哪一個SideTable中。在非嵌入式系統(tǒng)下,SideTables()中有 64 個SideTable。以下是SideTables()的定義:
// NSObject.mm
static objc::ExplicitInit<StripedMap<SideTable>> SideTablesMap;
static StripedMap<SideTable>& SideTables() {
return SideTablesMap.get();
}
所以,查找對象的引用計數(shù)表需要經過兩次哈希查找:
- ① 第一次根據(jù)當前對象的內存地址,經過哈希查找從
SideTables()中取出它所在的SideTable; - ② 第二次根據(jù)當前對象的內存地址,經過哈希查找從
SideTable中的refcnts中取出它的引用計數(shù)表。
使用多個SideTable+分離鎖技術方案是為了保證線程安全的同時兼顧訪問效率
以上就是iOS內存管理引用計數(shù)示例分析的詳細內容,更多關于iOS內存管理引用計數(shù)的資料請關注腳本之家其它相關文章!
相關文章
IOS使用UICollectionView實現(xiàn)無限輪播效果
這篇文章主要為大家詳細介紹了IOS使用UICollectionView實現(xiàn)無限輪播效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-03-03
iOS10語音識別框架SpeechFramework應用詳解
在iOS10系統(tǒng)了,apple開放了與語音識別相關的接口,開發(fā)者可以將其應用到自己的App中,實現(xiàn)用戶通過語音進行功能操作。 這篇文章主要介紹了iOS10語音識別框架SpeechFramework應用,需要的朋友可以參考下2016-09-09
MAUI模仿iOS多任務切換卡片滑動的交互實現(xiàn)代碼
這篇文章主要介紹了[MAUI]模仿iOS多任務切換卡片滑動的交互實現(xiàn),使用.NET MAU實現(xiàn)跨平臺支持,本項目可運行于Android、iOS平臺,需要的朋友可以參考下2023-05-05
iOS開發(fā)中關鍵字const/static/extern、UIKIT_EXTERN的區(qū)別和用法
這篇文章主要介紹了iOS 關鍵字const/static/extern、UIKIT_EXTERN區(qū)別和用法,需要的朋友可以參考下2017-12-12

