對(duì)象--id
typedef struct objc_object *id;
struct objc_object {
isa_t _Nonnull isa OBJC_ISA_AVAILABILITY;
};
arm64 架構(gòu)中的 isa_t 結(jié)構(gòu)體 (bits格式一樣,一些信息的位數(shù)不一樣)
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
uintptr_t nonpointer : 1; // 0 表示普通的 isa 指針谊路,1 表示使用優(yōu)化讹躯,存儲(chǔ)引用計(jì)數(shù)
uintptr_t has_assoc : 1; // 表示該對(duì)象是否包含 associated object,如果沒有,則析構(gòu)時(shí)會(huì)更快
uintptr_t has_cxx_dtor : 1; // 表示該對(duì)象是否有 C++ 或 ARC 的析構(gòu)函數(shù)潮梯,如果沒有骗灶,則析構(gòu)時(shí)更快
uintptr_t shiftcls : 33; // 類的指針
uintptr_t magic : 6; // 固定值為 0xd2,用于在調(diào)試時(shí)分辨對(duì)象是否未完成初始化秉馏。
uintptr_t weakly_referenced : 1; // 表示該對(duì)象是否有過(guò) weak 對(duì)象耙旦,如果沒有,則析構(gòu)時(shí)更快
uintptr_t deallocating : 1; // 表示該對(duì)象是否正在析構(gòu)
uintptr_t has_sidetable_rc : 1; // 標(biāo)記是否在 sidetable 中存有引用計(jì)數(shù)
uintptr_t extra_rc : 19; // 存儲(chǔ)引用計(jì)數(shù)值減一后的結(jié)果
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
};
引用計(jì)數(shù)
iOS引用計(jì)數(shù)管理之揭秘計(jì)數(shù)存儲(chǔ)
現(xiàn)在一個(gè)對(duì)象的引用計(jì)數(shù)管理有三種情況:
1沃饶、TaggedPointer -- 深入理解Tagged Pointer
2母廷、非nonpointer -- 純SideTable管理引用計(jì)數(shù)
3、nonpointer( 64 位設(shè)備的指針優(yōu)化)--(bits.extra_rc + SideTable)管理引用計(jì)數(shù)糊肤,(isa指針是占64位的琴昆,類的指針bits.shiftcls只占用33位,為了提高利用率馆揉,剩余的存儲(chǔ)內(nèi)存管理的相關(guān)數(shù)據(jù)內(nèi)容)
TaggedPointer的對(duì)象
對(duì)于 64 位程序业舍,為了節(jié)省內(nèi)存和提高執(zhí)行效率,蘋果提出了Tagged Pointer的概念升酣。
1舷暮、可以啟用Tagged Pointer的類對(duì)象有:NSDate、NSNumber噩茄、NSString下面。Tagged Pointer專門用來(lái)存儲(chǔ)小的對(duì)象。
2绩聘、Tagged Pointer指針的值不再是地址了沥割,而是真正的值。所以凿菩,實(shí)際上它不再是一個(gè)對(duì)象了机杜,它只是一個(gè)披著對(duì)象皮的普通變量而已。創(chuàng)建讀取效率非承乒龋快椒拗。
3、在環(huán)境變量中設(shè)置OBJC_DISABLE_TAGGED_POINTERS=YES強(qiáng)制不啟用Tagged Pointer获黔。
nonpointer
nonpointer:在64位系統(tǒng)中蚀苛,為了降低內(nèi)存使用,提升性能肢执,isa中有一部分字段用來(lái)存儲(chǔ)其他信息枉阵。
RetainCount源碼,直接體現(xiàn)了三種情況
inline uintptr_t
objc_object::rootRetainCount()
{
if (isTaggedPointer()) return (uintptr_t)this; // ??1预茄、TaggedPointer
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
if (bits.nonpointer) { // ??3兴溜、nonpointer--(bits.extra_rc + SideTable)管理引用計(jì)數(shù)
uintptr_t rc = 1 + bits.extra_rc;
if (bits.has_sidetable_rc) {
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
return sidetable_retainCount(); // ??2侦厚、純SideTable
}
2、SideTable
內(nèi)存管理主要結(jié)構(gòu)代碼
struct SideTable {
spinlock_t slock; // 保證原子操作的自旋鎖
RefcountMap refcnts; // 引用計(jì)數(shù)的 hash 表
weak_table_t weak_table; // weak 引用全局 hash 表
};
retainCount 是保存在一個(gè)無(wú)符號(hào)整形中
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()) {
// it->second 無(wú)符號(hào)整型(上面的圖)拙徽,真實(shí)的引用計(jì)數(shù)需要右移2位
refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
}
table.unlock();
return refcnt_result;
}
retain源碼
id
objc_object::sidetable_retain()
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];
table.lock();
size_t& refcntStorage = table.refcnts[this];
// 判斷是否溢出刨沦,溢出了就是引用計(jì)數(shù)太大了,不管理了
if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
// 引用計(jì)數(shù)加1膘怕,(上圖可知想诅,偏移2位,SIDE_TABLE_RC_ONE = 1<<2)
refcntStorage += SIDE_TABLE_RC_ONE;
}
table.unlock();
return (id)this;
}
release源碼
uintptr_t
objc_object::sidetable_release(bool performDealloc)
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];
bool do_dealloc = false;
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it == table.refcnts.end()) {
// ??(1)對(duì)象沒有引用計(jì)數(shù)(沒有被強(qiáng)引用)
do_dealloc = true;
// 標(biāo)記dealloc(上圖的第二位)
table.refcnts[this] = SIDE_TABLE_DEALLOCATING;
} else if (it->second < SIDE_TABLE_DEALLOCATING) {
// ??(2)引用計(jì)數(shù)為0
do_dealloc = true;
// 標(biāo)記dealloc(上圖的第二位岛心,或運(yùn)算)来破,
it->second |= SIDE_TABLE_DEALLOCATING;
} else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
// ??(3)正常引用計(jì)數(shù)減1,(上圖可知忘古,偏移2位徘禁,SIDE_TABLE_RC_ONE = 1<<2)
it->second -= SIDE_TABLE_RC_ONE;
}
table.unlock();
if (do_dealloc && performDealloc) {
// ??(4)do_dealloc為真,執(zhí)行dealloc方法
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return do_dealloc;
}
看后面幾個(gè)判斷髓堪。
(1) 如果對(duì)象記錄在引用計(jì)數(shù)表的最后一個(gè)(對(duì)象沒有引用計(jì)數(shù)):do_dealloc 設(shè)置為 true送朱,引用計(jì)數(shù)數(shù)值設(shè)置為 SIDE_TABLE_DEALLOCATING(二進(jìn)制 00000010)。
(2) 如果引用計(jì)數(shù)小于 SIDE_TABLE_DEALLOCATING(就是引用計(jì)數(shù)等于0)干旁,標(biāo)記dealloc驶沼。
(3) 如果引用計(jì)數(shù)大于>=1, 就it->second -= SIDE_TABLE_RC_ONE;就是-1。
(4) 如果 do_dealloc 和 performDealloc(傳入時(shí)就已經(jīng)為 true)都為 ture争群,執(zhí)行 SEL_dealloc 釋放對(duì)象回怜。方法返回 do_dealloc。
(5)調(diào)用父類dealloc换薄,直到根類NSobject
3鹉戚、nonpointer(bits.extra_rc + SideTable)
retain源碼
ALWAYS_INLINE id
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
//?????? 1、TaggedPointer
if (isTaggedPointer()) return (id)this;
bool sideTableLocked = false;
bool transcribeToSideTable = false;
isa_t oldisa;
isa_t newisa;
do {
transcribeToSideTable = false;
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
//?????? 2专控、純SideTable散列表方法
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
else return sidetable_retain();
}
if (slowpath(tryRetain && newisa.deallocating)) {
// 正在釋放
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
return nil;
}
//?????? 3、nonpointer(isa.extra_rc+ SideTable)
uintptr_t carry;
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // 應(yīng)用計(jì)數(shù)extra_rc++
// 如果newisa.extra_rc++ 溢出, carry==1
if (slowpath(carry)) {
// 溢出
if (!handleOverflow) {
ClearExclusive(&isa.bits); // 空操作(系統(tǒng)預(yù)留)
return rootRetain_overflow(tryRetain);// 再次調(diào)用rootRetain(tryRetain,YES)
}
// 執(zhí)行rootRetain_overflow會(huì)來(lái)到這里遏餐,就把extra_rc對(duì)應(yīng)的數(shù)值(一半)存到SideTable
if (!tryRetain && !sideTableLocked) sidetable_lock();
sideTableLocked = true;
transcribeToSideTable = true;
newisa.extra_rc = RC_HALF; // 溢出了伦腐,設(shè)置為一半,保存一半到SideTable
newisa.has_sidetable_rc = true; // 標(biāo)記借用SideTable存儲(chǔ)
}
// StoreExclusive保存newisa.bits到isa.bits失都,保存成功返回YES
// 這里while判斷一次就結(jié)束了
} while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
if (slowpath(transcribeToSideTable)) {
// 借位保存:把RC_HALF(一半)存入SideTable
sidetable_addExtraRC_nolock(RC_HALF);
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
return (id)this;
}
這個(gè)方法挺簡(jiǎn)單的:
1柏蘑、其實(shí)就是引用計(jì)數(shù)extra_rc++;
2粹庞、extra_rc滿了存一半到SideTable(arm64的extra_rc是19位咳焚,完全夠存)
借位保存方法
bool
objc_object::sidetable_addExtraRC_nolock(size_t delta_rc)
{
assert(isa.nonpointer);
SideTable& table = SideTables()[this];
size_t& refcntStorage = table.refcnts[this];
size_t oldRefcnt = refcntStorage;
// isa-side bits should not be set here
assert((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
assert((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);
// 系統(tǒng)計(jì)數(shù)極限了,直接true
if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true;
uintptr_t carry;
size_t newRefcnt =
addc(oldRefcnt, delta_rc << SIDE_TABLE_RC_SHIFT, 0, &carry);
if (carry) {
// 如果借位保存在這里還溢出庞溜,就當(dāng)做SIDE_TABLE_RC_PINNED次數(shù)(32或64最大位數(shù)-1的數(shù)值)
refcntStorage =
SIDE_TABLE_RC_PINNED | (oldRefcnt & SIDE_TABLE_FLAG_MASK);
return true;
}
else {
refcntStorage = newRefcnt;
return false;
}
}
release源碼
ALWAYS_INLINE bool
objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{
if (isTaggedPointer()) return false;
bool sideTableLocked = false;
isa_t oldisa;
isa_t newisa;
retry:
do {
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
// 這是2革半、SideTable散列表的
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return sidetable_release(performDealloc);
}
uintptr_t carry;
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc--
// 如果extra_rc==0碑定,extra_rc--會(huì)是負(fù)數(shù),carry=1
if (slowpath(carry)) {
goto underflow;
}
} while (slowpath(!StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits)));
if (slowpath(sideTableLocked)) sidetable_unlock();
return false;
underflow:
// newisa重新賦值
newisa = oldisa;
if (slowpath(newisa.has_sidetable_rc)) {
// 有借位保存又官,rootRelease_underflow重新進(jìn)入函數(shù)
if (!handleUnderflow) {
ClearExclusive(&isa.bits);
return rootRelease_underflow(performDealloc);
}
// 一些鎖的操作
if (!sideTableLocked) {
ClearExclusive(&isa.bits);
sidetable_lock();
sideTableLocked = true;
goto retry;
}
// 獲取借位引用次數(shù)延刘,(獲取次數(shù)最大RC_HALF)
size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);
if (borrowed > 0) {
// extra_rc--
newisa.extra_rc = borrowed - 1;
// 保存,就是StoreExclusive
// 如果&isa.bits和oldisa.bits相等六敬,那么就把newisa.bits的值賦給&isa.bits碘赖,并且返回true
bool stored = StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits);
if (!stored) {
// 保存失敗,重新試一次(重復(fù)的代碼)
isa_t oldisa2 = LoadExclusive(&isa.bits);
isa_t newisa2 = oldisa2;
if (newisa2.nonpointer) {
uintptr_t overflow;
newisa2.bits =
addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
if (!overflow) {
stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits,
newisa2.bits);
}
}
}
if (!stored) {
// 還是不成功外构,把次數(shù)放回SideTable普泡,重試retry
sidetable_addExtraRC_nolock(borrowed);
goto retry;
}
// release結(jié)束
sidetable_unlock();
return false;
}
else {
// 如果sidetable也有沒有次數(shù),然后就到下面dealloc階段了
}
}
// 如果沒有借位保存次數(shù)审编,來(lái)到這里
if (slowpath(newisa.deallocating)) {
// 如果對(duì)象已經(jīng)正在釋放撼班,報(bào)錯(cuò)警告:多次release
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return overrelease_error();
}
newisa.deallocating = true;
// 保存bits
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
if (slowpath(sideTableLocked)) sidetable_unlock();
__sync_synchronize();
if (performDealloc) {
// 調(diào)用dealloc
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return true;
}
NSobject dealloc源碼
void *objc_destructInstance(id obj)
{
if (obj) {
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
簡(jiǎn)單明確的干了三件事:
1、執(zhí)行object_cxxDestruct割笙,處理本類的成員變量(父類的由父類處理)权烧。
object_cxxDestruct里面最終for ( ; cls; cls = cls->superclass){執(zhí)行.cxx_destruct};
cxx_destruct最后執(zhí)行處理成員變量:
① strong的objc_storeStrong(&ivar, nil)release對(duì)象伤溉,ivar賦值nil般码,
② weak的objc_destroyWeak(& ivar)消除對(duì)象weak表中的ivar地址。
2乱顾、執(zhí)行_object_remove_assocations去除和這個(gè)對(duì)象assocate的對(duì)象(常用于category中添加帶變量的屬性板祝,這也是為什么沒必要remove一遍的原因。
3走净、執(zhí)行objc_clear_deallocating券时,清空引用計(jì)數(shù)表并清除弱引用表,將所有weak引用指nil(這也就是weak變量能安全置空的所在)伏伯。
對(duì)象釋放過(guò)程的簡(jiǎn)單總結(jié)--dealloc
1. 調(diào)用 -release :isa.bits.extra_rc由0繼續(xù)減一時(shí)候觸發(fā)dealloc橘洞,
(1)標(biāo)記對(duì)象isa.deallocating = true,對(duì)象正在銷毀说搅,生命周期即將結(jié)束炸枣,不能再有新的 weak 弱引用
(2)調(diào)用 [self dealloc] (MRC需要在dealloc方法中手動(dòng)釋放強(qiáng)引用的變量)
(3)superclass都調(diào)用dealloc,一直到根類(NSObject)
2. 根類 NSObject 調(diào) dealloc -> object_dispose()
(1)objc_destructInstance(obj);
(2)free(obj);
3. objc_destructInstance(obj)執(zhí)行三個(gè)操作
(1)if (cxx) object_cxxDestruct(obj); // 釋放變量
① strong ivar 執(zhí)行objc_storeStrong(&ivar, nil)release對(duì)象弄唧,ivar賦值nil适肠,
② weak ivar 執(zhí)行objc_destroyWeak(&ivar) >> storeWeak(&ivar, nil) 將ivar指向nil且ivar的地址從對(duì)象的weak表中刪除。
(2)if (assoc) _object_remove_assocations(obj); // 移除Associate關(guān)聯(lián)數(shù)據(jù)(這就是不需要手動(dòng)移除的原因)
(3)obj->clearDeallocating(); // 清空weak變量表且將所有引用指向nil候引、清空引用計(jì)數(shù)表
① 執(zhí)行 weak_clear_no_lock:清空weak變量表且將所有引用指向nil
② 執(zhí)行 table.refcnts.eraser:清空相關(guān)SideTable的引用計(jì)數(shù)表侯养。
objc_storeStrong源碼
是對(duì)一個(gè)strong指針再次賦值,比如
NSObject *obj = [NSObject new];
NSObject *obj2 = [NSObject new];
NSObject *strongObj = obj;
strongObj = obj2;//執(zhí)行objc_storeStrong(&strongObj, obj2);
{
NSObject *obj = [NSObject new];
} // 出了作用域,執(zhí)行objc_storeStrong(&obj, nil);
void
objc_storeStrong(id *location, id obj)
{
// 1澄干、當(dāng)前strong指針指向的位置找到舊對(duì)象逛揩,
id prev = *location;
if (obj == prev) {
return;
}
objc_retain(obj);// 2柠傍、新對(duì)象執(zhí)行retain,指針指向新對(duì)象息尺,
*location = obj;
objc_release(prev); //3携兵、 release舊對(duì)象
}
參考:
iOS進(jìn)階——iOS(Objective-C)內(nèi)存管理·二
ARC下dealloc過(guò)程及.cxx_destruct的探究