1 面試:全局變量和局部變量在內(nèi)存中是否有區(qū)別镰踏?如果有,是什么區(qū)別须蜗?
局部變量定義在局部的空間搀缠,全局變量定義在全局的區(qū)域蓉坎,存儲(chǔ)的區(qū)域不一樣
2 block 是否可以直接修改全局變量?
可以胡嘿,全局變量作用空間特別大,是公共的
3靜態(tài)區(qū)安全測(cè)試
全局靜態(tài)變量可以修改 只對(duì)文件有效钳踊,不是對(duì)類有效
新建一個(gè)LGPerson的類
//.h文件
#import <Foundation/Foundation.h>
static int personNum = 100;
NS_ASSUME_NONNULL_BEGIN
@interface LGPerson : NSObject
- (void)run;
+ (void)eat;
@end
//.m文件
@implementation LGPerson
- (void)run{
personNum ++;
NSLog(@"LGPerson內(nèi)部:%@-%p--%d",self,&personNum,personNum);
}
+ (void)eat{
personNum ++;
NSLog(@"LGPerson內(nèi)部:%@-%p--%d",self,&personNum,personNum);
}
- (NSString *)description{
return @"";
}
@end
再新建一個(gè)LGPerson的一個(gè)分類
#import "LGPerson.h"
NS_ASSUME_NONNULL_BEGIN
@interface LGPerson (LG)
- (void)cate_method;
@end
//.m文件
@implementation LGPerson (LG)
- (void)cate_method{
NSLog(@"LGPerson內(nèi)部:%@-%p--%d",self,&personNum,personNum);
}
@end
調(diào)用
// 100 可以修改
// 只針對(duì)文件有效 -
NSLog(@"vc:%p--%d",&personNum,personNum); // 100
personNum = 10000;
NSLog(@"vc:%p--%d",&personNum,personNum); // 10000
[[LGPerson new] run]; // 100 + 1 = 101
NSLog(@"vc:%p--%d",&personNum,personNum); // 10000
[LGPerson eat]; // 102
NSLog(@"vc:%p--%d",&personNum,personNum); // 10000
[[LGPerson alloc] cate_method];
打印結(jié)果為
2020-11-07 12:10:39.026774+0800 001---五大區(qū)Demo[5285:436872] vc:0x101bc23cc--100
2020-11-07 12:10:39.027201+0800 001---五大區(qū)Demo[5285:436872] vc:0x101bc23cc--10000
2020-11-07 12:10:39.027532+0800 001---五大區(qū)Demo[5285:436872] LGPerson內(nèi)部:-0x101bc23b0--101
2020-11-07 12:10:39.027790+0800 001---五大區(qū)Demo[5285:436872] vc:0x101bc23cc--10000
2020-11-07 12:10:39.028165+0800 001---五大區(qū)Demo[5285:436872] LGPerson內(nèi)部:LGPerson-0x101bc23b0--102
2020-11-07 12:10:39.028627+0800 001---五大區(qū)Demo[5285:436872] vc:0x101bc23cc--10000
2020-11-07 12:10:39.029702+0800 001---五大區(qū)Demo[5285:436872] LGPerson內(nèi)部:-0x101bc23d0--100
Tagged Pointer
先看一個(gè)面試題
- (void)taggedPointerDemo {
self.queue = dispatch_queue_create("com.cooci.cn", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<10000; i++) {
dispatch_async(self.queue, ^{
self.nameStr = [NSString stringWithFormat:@"cooci"];
NSLog(@"%@",self.nameStr);
});
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// 多線程
// setter getter
/**
retian newvalue
realase oldvalue
taggedpointer 影響
*/
NSLog(@"來(lái)了");
for (int i = 0; i<10000; i++) {
dispatch_async(self.queue, ^{
self.nameStr = [NSString stringWithFormat:@"cooci_和諧學(xué)習(xí)不急不躁"];
NSLog(@"%@",self.nameStr);
});
}
}
運(yùn)行taggedPointerDemo衷敌,代碼運(yùn)行沒(méi)問(wèn)題,點(diǎn)擊運(yùn)行另一塊代碼拓瞪,項(xiàng)目崩潰缴罗,之前多線程部分講到新值替換舊值的一瞬間可能會(huì)為空,導(dǎo)致程序崩潰祭埂,那么為什么上面的沒(méi)有問(wèn)題面氓,下面的崩潰呢?添加斷點(diǎn)蛆橡,打印得知:
當(dāng)self.nameStr為cooci時(shí)舌界,類型為NSTaggedPointerString
當(dāng)self.nameStr為cooci_和諧學(xué)習(xí)不急不躁時(shí),類型為NSCFString泰演,
打開(kāi)源碼呻拌,搜索release方法
__attribute__((aligned(16), flatten, noinline))
id
objc_retain(id obj)
{
if (!obj) return obj;
if (obj->isTaggedPointer()) return obj;
return obj->retain();
}
__attribute__((aligned(16), flatten, noinline))
void
objc_release(id obj)
{
if (!obj) return;
if (obj->isTaggedPointer()) return;
return obj->release();
}
可以發(fā)現(xiàn)不會(huì)對(duì)TaggedPointer類型進(jìn)行release,retain操作睦焕,一般是在8到10位左右長(zhǎng)度的小對(duì)象藐握,編譯讀取的時(shí)候更加直接,釋放是由系統(tǒng)直接回收
總結(jié):
1:Tagged Pointer專?用來(lái)存儲(chǔ)小的對(duì)象垃喊,例如NSNumber和NSDate
2:Tagged Pointer指針的值不再是地址了猾普,而是真正的值。所以本谜,實(shí)際上它不再 是一個(gè)對(duì)象了初家,它只是一個(gè)披著對(duì)象皮的普通變量而已。所以耕突,它的內(nèi)存并不存儲(chǔ) 在堆中笤成,也不需要malloc和free
3.在內(nèi)存讀取上有著3倍的效率,創(chuàng)建時(shí)比以前快106倍眷茁。
NONPOINTER_ISA
NONPOINTER_ISA同樣是蘋(píng)果公司對(duì)于內(nèi)存優(yōu)化的一種方案炕泳。用 64 bit 存儲(chǔ)一個(gè)內(nèi)存地址顯然是種浪費(fèi),畢竟很少有那么大內(nèi)存的設(shè)備上祈。于是可以優(yōu)化存儲(chǔ)方案培遵,用一部分額外空間存儲(chǔ)其他內(nèi)容浙芙。isa 指針第一位為 1 即表示使用優(yōu)化的 isa 指針,這里列出在x86_64架構(gòu)下的 64 位環(huán)境中 isa 指針結(jié)構(gòu)籽腕,arm64的架構(gòu)會(huì)有所差別嗡呼。
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 8
};
#endif
};
nonpointer:示是否對(duì)isa開(kāi)啟指針優(yōu)化。0代表是純isa指針皇耗,1代表除了地址外南窗,還包含了類的一些信息、對(duì)象的引用計(jì)數(shù)等
has_assoc:關(guān)聯(lián)對(duì)象標(biāo)志位郎楼,0沒(méi)有万伤,1存在。
has_cxx_dtor:該對(duì)象是否有C++或Objc的析構(gòu)器呜袁,如果有析構(gòu)函數(shù)敌买,則需要做一些析構(gòu)的邏輯處理,如果沒(méi)有阶界,則可以更快的釋放對(duì)象虹钮。
shiftcls:存在類指針的值,開(kāi)啟指針優(yōu)化的情況下膘融,arm64位中有33位來(lái)存儲(chǔ)類的指針
magic:判斷當(dāng)前對(duì)象是真的對(duì)象還是一段沒(méi)有初始化的空間
weakly_referenced:是否被指向或者曾經(jīng)指向一個(gè)ARC的弱變量芙粱,沒(méi)有弱引用的對(duì)象釋放的更快。
deallocating:標(biāo)志是否正在釋放內(nèi)存氧映。
has_sidetable_rc:是否有輔助的引用計(jì)數(shù)散列表宅倒。當(dāng)對(duì)象引?技術(shù)?于 10 時(shí),則需要借?該變量存儲(chǔ)進(jìn)位屯耸。
extra_rc:表示該對(duì)象的引?計(jì)數(shù)值拐迁,實(shí)際上是引?計(jì)數(shù)值減 1,例如疗绣,如果對(duì)象的引?計(jì)數(shù)為 10线召,那么 extra_rc 為 9。如果引?計(jì)數(shù)?于 10多矮,則需要使?到下?的 has_sidetable_rc缓淹。
內(nèi)存管理
retain是如何處理的
查看源碼
retain方法會(huì)調(diào)用方法rootRetain,
objc_object::retain()
{
ASSERT(!isTaggedPointer());
if (fastpath(!ISA()->hasCustomRR())) {
return rootRetain();
}
return ((id(*)(objc_object *, SEL))objc_msgSend)(this, @selector(retain));
}
繼續(xù)查看方法rootRetain
ALWAYS_INLINE id
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
if (isTaggedPointer()) return (id)this;
bool sideTableLocked = false;
bool transcribeToSideTable = false;
isa_t oldisa;
isa_t newisa;
// retain 引用計(jì)數(shù)處理
//
do {
transcribeToSideTable = false;
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
// 散列表的引用計(jì)數(shù)表 進(jìn)行處理 ++
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
if (rawISA()->isMetaClass()) return (id)this;
if (!tryRetain && sideTableLocked) sidetable_unlock();
if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
else return sidetable_retain();
}
// don't check newisa.fast_rr; we already called any RR overrides
if (slowpath(tryRetain && newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
return nil;
}
uintptr_t carry;
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
if (slowpath(carry)) {
// newisa.extra_rc++ overflowed
if (!handleOverflow) {
ClearExclusive(&isa.bits);
return rootRetain_overflow(tryRetain);
}
// Leave half of the retain counts inline and
// prepare to copy the other half to the side table.
if (!tryRetain && !sideTableLocked) sidetable_lock();
sideTableLocked = true;
transcribeToSideTable = true;
newisa.extra_rc = RC_HALF;
newisa.has_sidetable_rc = true;
}
} while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
if (slowpath(transcribeToSideTable)) {
// Copy the other half of the retain counts to the side table.
sidetable_addExtraRC_nolock(RC_HALF);
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
return (id)this;
}
首先判斷是不是nonpointer isa ,如果不是塔逃,引用計(jì)數(shù)存儲(chǔ)在散列表里面
這個(gè)散列表包含哪些東西呢讯壶?我們進(jìn)源碼看一下:
struct SideTable {
spinlock_t slock;
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(); }
// Address-ordered lock discipline for a pair of side tables.
template<HaveOld, HaveNew>
static void lockTwo(SideTable *lock1, SideTable *lock2);
template<HaveOld, HaveNew>
static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
可以看到散列表包含一把鎖 lock,引用計(jì)數(shù)表湾盗,弱引用表伏蚊。
在代碼中我們看到有SideTables,可以知道里面有多張散列表格粪,出于安全和性能問(wèn)題躏吊,里面有多張表氛改。數(shù)量不得知,大概64張比伏。
通過(guò)哈希結(jié)構(gòu)存儲(chǔ)多張散列表胜卤。
retain是對(duì)引用計(jì)數(shù)進(jìn)行處理,也是對(duì)散列表的引用計(jì)數(shù)表進(jìn)行處理 ++ --赁项。
如果是nonpointer isa葛躏,調(diào)用addc,對(duì)bits里面的位數(shù)進(jìn)行操作
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);
這個(gè)地方如果超負(fù)荷的話悠菜,就會(huì)對(duì)散列表進(jìn)行操作紫新,會(huì)把原來(lái)的一半存儲(chǔ)在extra_rc里面,另一半存儲(chǔ)在散列表里面
if (!tryRetain && !sideTableLocked) sidetable_lock();
sideTableLocked = true;
transcribeToSideTable = true;
newisa.extra_rc = RC_HALF;
newisa.has_sidetable_rc = true;
}
這里優(yōu)先isa李剖,是因?yàn)閑xtra_rc比散列表的操作要快
release操作
我們查看源碼,發(fā)現(xiàn)release方法會(huì)調(diào)用rootRelease方法囤耳。
objc_object::release()
{
ASSERT(!isTaggedPointer());
if (fastpath(!ISA()->hasCustomRR())) {
rootRelease();
return;
}
((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(release));
}
繼續(xù)查看rootRelease方法篙顺,其原理與retain一致,這里需要注意一點(diǎn)充择,就是dealloc方法是在哪里調(diào)用的呢德玫?
是在release調(diào)用的時(shí)候,當(dāng)引用計(jì)數(shù)為0的時(shí)候椎麦,會(huì)通過(guò)objc_msgSend方式調(diào)用
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)) {
ClearExclusive(&isa.bits);
if (rawISA()->isMetaClass()) return false;
if (sideTableLocked) sidetable_unlock();
return sidetable_release(performDealloc);
}
// don't check newisa.fast_rr; we already called any RR overrides
uintptr_t carry;
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc--
if (slowpath(carry)) {
// don't ClearExclusive()
goto underflow;
}
} while (slowpath(!StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits)));
if (slowpath(sideTableLocked)) sidetable_unlock();
return false;
underflow:
// newisa.extra_rc-- underflowed: borrow from side table or deallocate
// abandon newisa to undo the decrement
newisa = oldisa;
if (slowpath(newisa.has_sidetable_rc)) {
if (!handleUnderflow) {
ClearExclusive(&isa.bits);
return rootRelease_underflow(performDealloc);
}
// Transfer retain count from side table to inline storage.
if (!sideTableLocked) {
ClearExclusive(&isa.bits);
sidetable_lock();
sideTableLocked = true;
// Need to start over to avoid a race against
// the nonpointer -> raw pointer transition.
goto retry;
}
// Try to remove some retain counts from the side table.
size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);
// To avoid races, has_sidetable_rc must remain set
// even if the side table count is now zero.
if (borrowed > 0) {
// Side table retain count decreased.
// Try to add them to the inline count.
newisa.extra_rc = borrowed - 1; // redo the original decrement too
bool stored = StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits);
if (!stored) {
// Inline update failed.
// Try it again right now. This prevents livelock on LL/SC
// architectures where the side table access itself may have
// dropped the reservation.
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) {
// Inline update failed.
// Put the retains back in the side table.
sidetable_addExtraRC_nolock(borrowed);
goto retry;
}
// Decrement successful after borrowing from side table.
// This decrement cannot be the deallocating decrement - the side
// table lock and has_sidetable_rc bit ensure that if everyone
// else tried to -release while we worked, the last one would block.
sidetable_unlock();
return false;
}
else {
// Side table is empty after all. Fall-through to the dealloc path.
}
}
// Really deallocate.
if (slowpath(newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return overrelease_error();
// does not actually return
}
newisa.deallocating = true;
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
if (slowpath(sideTableLocked)) sidetable_unlock();
__c11_atomic_thread_fence(__ATOMIC_ACQUIRE);
if (performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
}
return true;
}
retainCount
先看一個(gè)面試題:
NSObject *objc = [NSObject alloc]; // 0
NSLog(@"%ld",objc.retainCount); // 1
NSLog(@"%ld",objc.retainCount); // 1
兩次打印結(jié)果都為1宰僧,那么這里請(qǐng)問(wèn)alloc出來(lái)的對(duì)象引用計(jì)數(shù)為1 是否正確?答案錯(cuò)誤观挎,alloc出來(lái)的對(duì)象引用計(jì)數(shù)為0.
因?yàn)閍lloc 源碼里沒(méi)有對(duì)引用計(jì)數(shù)進(jìn)行操作琴儿,那我們看下retainCount的源碼,bits.extra_rc為0,可以發(fā)現(xiàn)alloc出來(lái)的對(duì)象引用計(jì)數(shù)為0嘁捷,打印出來(lái)為1是因?yàn)檫@里會(huì)默認(rèn)給他一個(gè)1造成。再次打印一次retainCount還是為1,是因?yàn)檫@里的bits.extra_rc并沒(méi)有存值雄嚣,還是為0晒屎,所以打印出來(lái)的還是1。
inline uintptr_t
objc_object::rootRetainCount() // 1
{
if (isTaggedPointer()) return (uintptr_t)this;
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
if (bits.nonpointer) {
// bits.extra_rc = 0;
//
uintptr_t rc = 1 + bits.extra_rc; // isa
if (bits.has_sidetable_rc) {
rc += sidetable_getExtraRC_nolock(); // 散列表
}
sidetable_unlock();
return rc; // 1
}
sidetable_unlock();
return sidetable_retainCount();
}
dealloc
下面我們分析dealloc操作源碼缓升,這里對(duì)調(diào)用方法rootDealloc鼓鲁,然后是object_dispose方法
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
object_dispose方法
id
object_dispose(id obj)
{
if (!obj) return nil;
// weak
// cxx
// 關(guān)聯(lián)對(duì)象
// ISA 64
objc_destructInstance(obj);
free(obj);
return nil;
}
在free方法之前走方法objc_destructInstance
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
然后走方法clearDeallocating,在這里進(jìn)行散列表清空港谊,如果含有弱引用計(jì)數(shù)骇吭,調(diào)用方法clearDeallocating_slow,繼續(xù)清理引用計(jì)數(shù)表
inline void
objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}
assert(!sidetable_present());
}
objc_object::clearDeallocating_slow()
{
ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
if (isa.has_sidetable_rc) { // 清理引用計(jì)數(shù)表
table.refcnts.erase(this);
}
table.unlock();
}
總結(jié)一下上面的流程可以如下所示:
強(qiáng)引用
我們知道打破block強(qiáng)引用的話歧寺,可以用weakSelf解決绵跷,self指向block膘螟,block指向weakSelf,來(lái)打破循環(huán)引用碾局,注意一點(diǎn)荆残,這個(gè)block捕獲的是weakSelf這個(gè)臨時(shí)變量的指針地址,不是這個(gè)對(duì)象净当,weakSelf和self是兩個(gè)不同的指針地址内斯,指向了同一個(gè)對(duì)象,所以打破了這里的循環(huán)引用像啼。
self -> block -> weakSelf(臨時(shí)變量的指針地址)
下面我們?cè)倏匆粋€(gè)例子俘闯,這里會(huì)形成一個(gè)強(qiáng)引用,self持有timer忽冻,timer持有self
self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(fireHome) userInfo:nil repeats:YES];
如果我們也像上面那樣操作用weakSelf是否能解決呢真朗?改成下面這樣
__weak typeof(self) weakSelf = self; // weak
self.timer = [NSTimer timerWithTimeInterval:1 target:weakSelf selector:@selector(fireHome) userInfo:nil repeats:YES];
答案是不能,因?yàn)檫@里timer強(qiáng)持有的事weakSelf指針指向的對(duì)象僧诚,也就是self指針指向的對(duì)象遮婶。
自動(dòng)釋放池
自動(dòng)釋放池始于MRC時(shí)代,主要是用于 自動(dòng) 對(duì) 釋放池內(nèi) 對(duì)象 進(jìn)行引用計(jì)數(shù)-1的操作湖笨,即自動(dòng)執(zhí)行release方法,在MRC中使用autoreleasepool必須在代碼塊內(nèi)部手動(dòng)為對(duì)象調(diào)用autorelease把對(duì)象加入到的自動(dòng)釋放池旗扑,系統(tǒng)會(huì)自動(dòng)在代碼塊結(jié)束后,對(duì)加入自動(dòng)釋放池中的對(duì)象發(fā)送一個(gè)release消息.無(wú)需手動(dòng)調(diào)用release.
通過(guò)clang生成cpp文件慈省,查看
int main(int argc, const char * argv[]) {
@autoreleasepool {
}
}
在cpp文件顯示為這個(gè)結(jié)構(gòu)體的調(diào)用臀防,也就是用走方法__AtAutoreleasePool和~__AtAutoreleasePool()
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
先看方法objc_autoreleasePoolPush,搜索源碼
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
繼續(xù)往下點(diǎn)擊
這里看到繼承自AutoreleasePoolPageData边败,我們看下AutoreleasePoolPageData是什么
class AutoreleasePoolPage;
struct AutoreleasePoolPageData
{
magic_t const magic; // 16
__unsafe_unretained id *next; //8
pthread_t const thread; // 8
AutoreleasePoolPage * const parent; //8
AutoreleasePoolPage *child; //8
uint32_t const depth; // 4
uint32_t hiwat; // 4
AutoreleasePoolPageData(__unsafe_unretained id* _next, pthread_t _thread, AutoreleasePoolPage* _parent, uint32_t _depth, uint32_t _hiwat)
: magic(), next(_next), thread(_thread),
parent(_parent), child(nil),
depth(_depth), hiwat(_hiwat)
{
}
};
struct magic_t {
static const uint32_t M0 = 0xA1A1A1A1; // 靜態(tài)變量不在這里存儲(chǔ)
# define M1 "AUTORELEASE!"
static const size_t M1_len = 12;
uint32_t m[4]; //4*4 = 16
magic_t() {
ASSERT(M1_len == strlen(M1));
ASSERT(M1_len == 3 * sizeof(m[1]));
m[0] = M0;
strncpy((char *)&m[1], M1, M1_len);
}
~magic_t() {
// Clear magic before deallocation.
// This prevents some false positives in memory debugging tools.
// fixme semantically this should be memset_s(), but the
// compiler doesn't optimize that at all (rdar://44856676).
volatile uint64_t *p = (volatile uint64_t *)m;
p[0] = 0; p[1] = 0;
}
AutoreleasePoolPageData是個(gè)結(jié)構(gòu)體,里面有自己的一些成員變量袱衷,里面的字段的意義如下
magic 用來(lái)校驗(yàn) AutoreleasePoolPage 的結(jié)構(gòu)是否完整;
? next 指向最新添加的 autoreleased 對(duì)象的下一個(gè)位置,初始化時(shí)指向
begin() ;
? thread 指向當(dāng)前線程;
? parent 指向父結(jié)點(diǎn)笑窜,第一個(gè)結(jié)點(diǎn)的 parent 值為 nil ;
? child 指向子結(jié)點(diǎn)祟昭,最后一個(gè)結(jié)點(diǎn)的 child 值為 nil ;
? depth 代表深度,從 0 開(kāi)始怖侦,往后遞增 1;
? hiwat 代表 high water mark 最大入棧數(shù)量標(biāo)記
總結(jié)一下:
AutoreleasePoolPage其實(shí)就是一個(gè)雙向鏈表結(jié)構(gòu),AutoreleasePoolPage(自動(dòng)釋放池頁(yè)) 用來(lái)存放 autorelease 的對(duì)象篡悟,但是每一頁(yè)的大小是有限制的,假如某個(gè)AutoreleasePoolPage頁(yè)中需要存放的autorelease 的對(duì)象過(guò)多,一頁(yè)存放不完,所以它就需要指向父結(jié)點(diǎn)點(diǎn),在指向父結(jié)點(diǎn)里的AutoreleasePoolPage頁(yè)中繼續(xù)存放.
那么每一頁(yè)大小是多少呢?
class AutoreleasePoolPage : private AutoreleasePoolPageData
{
friend struct thread_data_t;
public:
static size_t const SIZE =
#if PROTECT_AUTORELEASEPOOL
PAGE_MAX_SIZE; // must be multiple of vm page size
#else
PAGE_MIN_SIZE; // size and alignment, power of 2
.
.
.
#endif
}
有個(gè)PAGE_MAX_SIZE,點(diǎn)擊進(jìn)去是4096.原來(lái)每一頁(yè)AutoreleasePoolPage可以存放4096個(gè)字節(jié).一共4096個(gè)字節(jié)匾寝, (4096 - AutoreleasePoolPage 中自己成員變量所占的字節(jié))/每個(gè)對(duì)象中所占的字節(jié). (4096 - 56)/8 = 505. 好的,每一AutoreleasePoolPage可以存放505個(gè)對(duì)象搬葬。
一個(gè)autoreleasePoolPage最多能添加504個(gè)8字節(jié)對(duì)象是否正確?
第一頁(yè)是504+1個(gè)對(duì)象艳悔,這一個(gè)不是你添加進(jìn)去的急凰,是一個(gè)標(biāo)記,是邊界,也就是第一頁(yè)最多能添加504個(gè)對(duì)象抡锈,其他頁(yè)最多能添加505個(gè)對(duì)象疾忍。
總結(jié)
在APP中,整個(gè)主線程是運(yùn)行在一個(gè)自動(dòng)釋放池中的床三。
main函數(shù)中的自動(dòng)釋放池的作用:這個(gè)池塊給出了一個(gè)pop點(diǎn)來(lái)顯式的告訴我們這里有一個(gè)釋放點(diǎn)一罩,如果你的main在初始化的過(guò)程中有別的內(nèi)容可以放在這里。
使用@autoreleasepool標(biāo)記撇簿,調(diào)用push()方法聂渊。
沒(méi)有hotpage,調(diào)用()四瘫,設(shè)置EMPTY_POOL_PLACEHOLDER汉嗽。
因?yàn)樵O(shè)置了EMPTY_POOL_PLACEHOLDER,所以會(huì)設(shè)置本頁(yè)為hotpage找蜜,添加邊界標(biāo)記POOL_BOUNDARY饼暑,最后添加obj。
繼續(xù)有對(duì)象調(diào)用autorelease洗做,此時(shí)已經(jīng)有了page弓叛,調(diào)用page->add(obj)。
如果page滿了竭望,調(diào)用autoreleaseFullPage()創(chuàng)建新page,重復(fù)第6點(diǎn)裕菠。
到達(dá)autoreleasePool邊界咬清,調(diào)用pop方法,通常情況下會(huì)釋放掉POOL_BOUNDARY之后的所有對(duì)象