iOS內(nèi)存管理(一)、內(nèi)存分區(qū)和引用計(jì)數(shù)
iOS內(nèi)存管理(二)alloc、retain、release、dealloc
一、內(nèi)存分區(qū)

0xc0000000轉(zhuǎn)化出來(lái),正好為3GB,所以我們的運(yùn)行內(nèi)存最多為3GB
在動(dòng)態(tài)分配內(nèi)存的時(shí)候,棧區(qū)的棧幀不斷往下走,而堆區(qū)隨著內(nèi)存開(kāi)辟越多會(huì)不斷往上走,當(dāng)它們重合的時(shí)候,就形成了堆棧溢出。
在dyld加載可執(zhí)行文件到內(nèi)存的時(shí)候,它會(huì)將加載的數(shù)據(jù)給分別存放到.bss、.data、.text段。
iOS的內(nèi)存分區(qū)指RAM中的內(nèi)存分區(qū),它主要分為五大區(qū):

二、內(nèi)存管理方案 isa_t
TaggedPointer:小對(duì)象-NSNumber,NSDate以及長(zhǎng)度短的NSSTring
NONPOINTER_ISA:非指針型isa
散列表:引用計(jì)數(shù)表、弱引用表
在這里我們先介紹一下isa,它在源碼中的結(jié)構(gòu)為(只看arm64的)
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
struct {
//0表示普通的isa指針,1表示使用優(yōu)化,存儲(chǔ)引用計(jì)數(shù)
uintptr_t nonpointer : 1; \
//表示該對(duì)象是否包含關(guān)聯(lián)對(duì)象,如果沒(méi)有,則析構(gòu)時(shí)會(huì)更快
uintptr_t has_assoc : 1; \
//表示該對(duì)象是否有C++或ARC的析構(gòu)函數(shù),如果沒(méi)有,則析構(gòu)時(shí)更快
uintptr_t has_cxx_dtor : 1; \
//類的指針
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
//固定值為0xd2,用于在調(diào)試時(shí)分辨對(duì)象是否未完成初始化
uintptr_t magic : 6; \
//表示該對(duì)象是否有過(guò)weak對(duì)象,如果沒(méi)有,則析構(gòu)時(shí)更快
uintptr_t weakly_referenced : 1; \
//表示該對(duì)象是否正在析構(gòu)
uintptr_t deallocating : 1; \
//表示該對(duì)象的引用計(jì)數(shù)值是否過(guò)大需要額外存儲(chǔ)到sidetable中
uintptr_t has_sidetable_rc : 1; \
//存儲(chǔ)最多2^8-1的引用計(jì)數(shù)值,存儲(chǔ)sidetable以外的引用計(jì)數(shù)值減1的結(jié)果
uintptr_t extra_rc : 19
};
};
三、TaggedPointer
TaggedPointer常用來(lái)存儲(chǔ)小對(duì)象如NSNumber,NSDate以及長(zhǎng)度短的NSSTring,TaggedPointer指針的值不再是地址了,而是真正的值,所以實(shí)際上它不再是一個(gè)對(duì)象了,它只是披著對(duì)象皮的普通變量而已!所以,它的內(nèi)存并不存儲(chǔ)在堆中,也不需要malloc和free
TaggedPointer內(nèi)存讀取是讀取對(duì)象的3倍,創(chuàng)建對(duì)象過(guò)程是創(chuàng)建對(duì)象的106倍
一般TaggedPointer的打印的結(jié)構(gòu)為tag+值+值類型
源碼中,如果為taggedPointer,直接返回非類類型
inline bool objc_object::isClass()
{
//如果是TaggedPointer,返回false
if (isTaggedPointer()) return false;
return ISA()->isMetaClass();
}
inline bool objc_object::isTaggedPointer()
{
return _objc_isTaggedPointer(this);
}
static inline bool _objc_isTaggedPointer(const void * _Nullable ptr)
{
return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}
define _OBJC_TAG_MASK (1UL<<63)
在_objc_isTaggedPointer函數(shù)中,將自身指針值與1...0進(jìn)行按位與操作,如果還是1...0,則是使用了taggedPointer機(jī)制,也就是說(shuō),在iOS中,判斷是否taggedPoint,就是看其最高位是否為1。
拓展:在MACOS中,由于define _OBJC_TAG_MASK 1UL,所以,判斷在MACOS中是否為taggedPoint,就是看其最低位是否為1
我們?cè)倏催@個(gè)函數(shù)
static inline void * _Nonnull
_objc_makeTaggedPointer(objc_tag_index_t tag, uintptr_t value)
{
if (tag <= OBJC_TAG_Last60BitPayload) {
uintptr_t result =
(_OBJC_TAG_MASK |
((uintptr_t)tag << _OBJC_TAG_INDEX_SHIFT) |
((value << _OBJC_TAG_PAYLOAD_RSHIFT) >> _OBJC_TAG_PAYLOAD_LSHIFT));
return _objc_encodeTaggedPointer(result);
} else {
uintptr_t result =
(_OBJC_TAG_EXT_MASK |
((uintptr_t)(tag - OBJC_TAG_First52BitPayload) << _OBJC_TAG_EXT_INDEX_SHIFT) |
((value << _OBJC_TAG_EXT_PAYLOAD_RSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_LSHIFT));
return _objc_encodeTaggedPointer(result);
}
}
我們不看其它部分,最終不論是哪一種形式,最后函數(shù)都會(huì)調(diào)用_objc_encodeTaggedPointer(result),我們?cè)賮?lái)看這個(gè)函數(shù)
uintptr_t objc_debug_taggedpointer_obfuscator = 0;
//編碼方法
static inline void * _Nonnull_objc_encodeTaggedPointer(uintptr_t ptr)
{
return (void *)(objc_debug_taggedpointer_obfuscator ^ ptr);
}
//解碼方法
static inline uintptr_t _objc_decodeTaggedPointer(const void * _Nullable ptr)
{
return (uintptr_t)ptr ^ objc_debug_taggedpointer_obfuscator;
}
編碼方法中,與0進(jìn)行按位異或進(jìn)行編碼,解碼方法中,同樣與0進(jìn)行按位異或進(jìn)行解碼
在最新版的objc4-781源碼中,關(guān)于objc_debug_taggedpointer_obfuscator進(jìn)行更多一步的操作,在_read_images方法中,調(diào)用initializeTaggedPointerObfuscator()對(duì)objc_debug_taggedpointer_obfuscator進(jìn)行了初始化(做了代碼混淆),我們看看這個(gè)函數(shù)的源碼:
static void
initializeTaggedPointerObfuscator(void)
{
if (sdkIsOlderThan(10_14, 12_0, 12_0, 5_0, 3_0) || DisableTaggedPointerObfuscation) {
//老版本直接為0
objc_debug_taggedpointer_obfuscator = 0;
} else {
//新版本給一個(gè)隨機(jī)數(shù),和_OBJC_TAG_MASK按位取反后的值進(jìn)行與操作
arc4random_buf(&objc_debug_taggedpointer_obfuscator,
sizeof(objc_debug_taggedpointer_obfuscator));
objc_debug_taggedpointer_obfuscator &= ~_OBJC_TAG_MASK;
}
}
define _OBJC_TAG_MASK (1UL<<63)
所以按照現(xiàn)在的源碼結(jié)構(gòu),我們?nèi)绻胍吹皆瓉?lái)的tag+值+值類型結(jié)構(gòu),需要我們將_objc_decodeTaggedPointer方法拷貝出來(lái),自己對(duì)它進(jìn)行解碼操作,
- (void)taggedPointerTest{
int num1 = 22;
long num2 = 55;
float num3 = 2.0f;
double num4 = 4.0;
double num5 = 22.33;
NSNumber *number1 = @(num1);
NSNumber *number2 = @(num2);
NSNumber *number3 = @(num3);
NSNumber *number4 = @(num4);
NSNumber *number5 = @(num5);
NSLog(@"所屬類:%@---指針:%p---值:%@---0x%lx",object_getClass(number1),number1,number1,_objc_decodeTaggedPointer(number1));
NSLog(@"所屬類:%@---指針:%p---值:%@---0x%lx",object_getClass(number2),number2,number2,_objc_decodeTaggedPointer(number2));
NSLog(@"所屬類:%@---指針:%p---值:%@---0x%lx",object_getClass(number3),number3,number3,_objc_decodeTaggedPointer(number3));
NSLog(@"所屬類:%@---指針:%p---值:%@---0x%lx",object_getClass(number4),number4,number4,_objc_decodeTaggedPointer(number4));
NSLog(@"所屬類:%@---指針:%p---值:%@---0x%lx",object_getClass(number5),number5,number5,_objc_decodeTaggedPointer(number5));
}
extern uintptr_t objc_debug_taggedpointer_obfuscator;
uintptr_t
_objc_decodeTaggedPointer(id ptr)
{
return (uintptr_t)ptr ^ objc_debug_taggedpointer_obfuscator;
}
//打印結(jié)果
// 所屬類:__NSCFNumber---指針:0xfd8919a30310b479---值:22---0xb000000000000162
// 所屬類:__NSCFNumber---指針:0xfd8919a30310b668---值:55---0xb000000000000373
// 所屬類:__NSCFNumber---指針:0xfd8919a30310b53f---值:2---0xb000000000000024
// 所屬類:__NSCFNumber---指針:0xfd8919a30310b55e---值:4---0xb000000000000045
// 所屬類:__NSCFNumber---指針:0x6000013d0d80---值:22.33---0x4d8979a3022db89b
我們可以看到指針打印出來(lái)的值已經(jīng)沒(méi)有原來(lái)的那種含義,因?yàn)楝F(xiàn)在源碼中已做了代碼混淆。而在我們自己解碼出來(lái)的值中,可以看到最后一位代表它的類型,2表示int類型,3代表long類型,4代表float類型,5代表double類型,而對(duì)于復(fù)雜的浮點(diǎn)數(shù),如num5,最后一位并不能確定其類型。除此以外,在類型前面則顯示的是它的值的16進(jìn)制值。
NONPOINTER_ISA
若isa_t中位域中的nonpointer為1,表示優(yōu)化過(guò)的isa,用于存儲(chǔ)引用計(jì)數(shù)
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
struct {
//0表示普通的isa指針,1表示使用優(yōu)化,存儲(chǔ)引用計(jì)數(shù)
uintptr_t nonpointer : 1; \
//表示該對(duì)象是否包含關(guān)聯(lián)對(duì)象,如果沒(méi)有,則析構(gòu)時(shí)會(huì)更快
uintptr_t has_assoc : 1; \
//表示該對(duì)象是否有C++或ARC的析構(gòu)函數(shù),如果沒(méi)有,則析構(gòu)時(shí)更快
uintptr_t has_cxx_dtor : 1; \
//類的指針
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
//固定值為0xd2,用于在調(diào)試時(shí)分辨對(duì)象是否未完成初始化
uintptr_t magic : 6; \
//表示該對(duì)象是否有過(guò)weak對(duì)象,如果沒(méi)有,則析構(gòu)時(shí)更快
uintptr_t weakly_referenced : 1; \
//表示該對(duì)象是否正在析構(gòu)
uintptr_t deallocating : 1; \
//表示該對(duì)象的引用計(jì)數(shù)值是否過(guò)大需要額外存儲(chǔ)到sidetable中
uintptr_t has_sidetable_rc : 1; \
//存儲(chǔ)最多2^8-1的引用計(jì)數(shù)值,存儲(chǔ)sidetable以外的引用計(jì)數(shù)值減1的結(jié)果
uintptr_t extra_rc : 19
};
};
現(xiàn)在我們來(lái)研究retainCount在源碼中的實(shí)現(xiàn)
- (NSUInteger)retainCount {
return _objc_rootRetainCount(self);
}
uintptr_t _objc_rootRetainCount(id obj)
{
//判斷對(duì)象是否為空,為空斷在這里
ASSERT(obj);
return obj->rootRetainCount();
}
inline uintptr_t objc_object::rootRetainCount()
{
//如果是taggedPointer 直接返回指針值
if (isTaggedPointer()) return (uintptr_t)this;
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
//如果是優(yōu)化后的指針(存儲(chǔ)引用計(jì)數(shù))
if (bits.nonpointer) {
//將extra_rc值加1,rc是引用計(jì)數(shù)
uintptr_t rc = 1 + bits.extra_rc;
//如果有sidetable
if (bits.has_sidetable_rc) {
//獲取sidetable中存儲(chǔ)的引用計(jì)數(shù)值
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
//如果不是優(yōu)化后的指針,那么引用計(jì)數(shù)從sidetable中返回
return sidetable_retainCount();
}
//從sideTable中取出引用計(jì)數(shù)值,不上鎖,而且不加1,因?yàn)樵谇懊娴暮瘮?shù)中已經(jīng)+1
size_t objc_object::sidetable_getExtraRC_nolock()
{
ASSERT(isa.nonpointer);
SideTable& table = SideTables()[this];
RefcountMap::iterator it = table.refcnts.find(this);
if (it == table.refcnts.end()) return 0;
//從refcnts中取出值,然后右移兩位獲取到引用計(jì)數(shù)
//這里為什么要右移兩位,因?yàn)樽畹臀淮鎯?chǔ)了是否有弱引用,低二位存儲(chǔ)了SideTable是否在析構(gòu),所以需要右移兩位
else return it->second >> SIDE_TABLE_RC_SHIFT;
}
//從sideTable中取出引用計(jì)數(shù)值,上鎖,而且要加1
uintptr_t objc_object::sidetable_retainCount()
{
SideTable& table = SideTables()[this];
size_t refcnt_result = 1;
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
//從refcnts中取出值,然后右移兩位獲取到引用計(jì)數(shù)
//這里為什么要右移兩位,因?yàn)樽畹臀淮鎯?chǔ)了是否有弱引用,低二位存儲(chǔ)了SideTable是否在析構(gòu),所以需要右移兩位
refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
}
table.unlock();
return refcnt_result;
}
#define SIDE_TABLE_WEAKLY_REFERENCED (1UL<<0)
#define SIDE_TABLE_DEALLOCATING (1UL<<1)
#define SIDE_TABLE_RC_ONE (1UL<<2)
#define SIDE_TABLE_RC_SHIFT 2
//SideTable的數(shù)據(jù)結(jié)構(gòu)
struct SideTable {
//鎖
spinlock_t slock;
//存儲(chǔ)了引用計(jì)數(shù)的哈希表
RefcountMap refcnts;
//弱引用表
weak_table_t weak_table;
SideTable() {
memset(&weak_table, 0, sizeof(weak_table));
}
~SideTable() {
_objc_fatal("Do not delete SideTable.");
}
void lock() { slock.lock(); }
void unlock() { slock.unlock(); }
void forceReset() { slock.forceReset(); }
template<HaveOld, HaveNew>
static void lockTwo(SideTable *lock1, SideTable *lock2);
template<HaveOld, HaveNew>
static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
//SideTable中RefcountMap的定義,它是一張哈希表
typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;
RefcountMap refcnts:它是一張哈希表,查詢的時(shí)候使用對(duì)象的地址作為key,經(jīng)過(guò)哈希算法,得到一個(gè)值,這個(gè)值的最低位存儲(chǔ)了是否有弱引用,低二位存儲(chǔ)了是否正在析構(gòu),將這個(gè)值右移兩位,如果nonpointer為0,右移兩位后加1,則是引用計(jì)數(shù)值;如果nonpointer為1,我們從isa_t的extra_rc中取出值,加1,再加上右移兩位的值,則得到了引用計(jì)數(shù)值
從上面的源碼中,對(duì)于非TaggedPointer對(duì)象,我們可以得出以下結(jié)論:
- 如果
nonpointer為0,表示isa未優(yōu)化過(guò),不作為存儲(chǔ)引用計(jì)數(shù),那么引用計(jì)數(shù)值都存放在SideTable中成員refcnts中,從refcnts中取出值加1,則得到了我們的引用計(jì)數(shù)值 - 如果
nonpointer為1,表示isa優(yōu)化過(guò),存儲(chǔ)了部分引用計(jì)數(shù)值,我們從isa_t的extra_rc中取出值,加1,再?gòu)腟ideTable中成員refcnts中取出值右移兩位,加上前面的值,則得到了引用計(jì)數(shù)值