《iOS底層原理文章匯總》
上一篇文章iOS-底層原理33-內(nèi)存管理(上)介紹了內(nèi)存管理,本文接著介紹內(nèi)存管理(下)和RunLoop
class AutoreleasePoolPage : private AutoreleasePoolPageData
1.指針->棧的結(jié)構(gòu)
2.對象釋放 + POOL_BOUNDARY 哨兵 -> 邊界
3.頁的結(jié)構(gòu)+雙向鏈表
4.線程有關(guān)系
1.AutoreleasePool
I.Clang編譯底層源碼@autoreleasepool{},相當(dāng)于__AtAutoreleasePool __autoreleasepool;
,{}表示作用域渣淳,__AtAutoreleasePool為結(jié)構(gòu)體,由構(gòu)造函數(shù)和析構(gòu)函數(shù)組成
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
image.png
II.自動釋放池的結(jié)構(gòu)伴箩,AutoreleasePoolPage::push(),
class AutoreleasePoolPage : private AutoreleasePoolPageData
入愧,::相當(dāng)于類調(diào)用類方法image.png
image.png
image.png
III.自動釋放池的結(jié)構(gòu)分析
AutoreleasePoolPage繼承于AutoreleasePoolPageData,初始化方法來自于AutoreleasePoolPageData
image.png
image.png
image.png
image.png
內(nèi)存平移56個字節(jié)
image.png
image.png
image.png
image.png
IV.自動釋放池釋放原理,################ POOL 0x103009038 哨兵對象 邊界 越界嗤谚,3*16+8 = 56 AutoreleasePool自身的begin()位置棺蛛,哨兵對象表示邊界
image.png
若放504個對象正好一頁,504個對象加上一個哨兵對象505個對象
image.png
若放505個對象巩步,重新開始一頁旁赊,指針地址從38開始,新的一頁,每一頁的大小為4096個字節(jié)=505*8=4040+自身大小56=4096
image.png
define I386_PGBYTES 4096
define PAGE_SIZE I386_PGBYTES
define PAGE_MIN_SIZE PAGE_SIZE
image.png
從第二頁開始可以壓入多少個對象?哨兵對象在一個自動釋放池中存在幾個?
image.png
V.對象是怎么壓棧的椅野,頁是怎么關(guān)聯(lián)的终畅?
0.判斷頁面存不存在
1.push:壓棧進(jìn)來一個邊界+當(dāng)前頁,變?yōu)閔otPage,表示當(dāng)前正在操作的頁面竟闪,coldPage表示當(dāng)前沒有被操作的頁面
2.判斷頁是否滿了离福,沒滿,繼續(xù)添加頁面炼蛤,滿了新建頁妖爷,設(shè)置為coldPage
child是子的一頁,新的一頁
image.png
image.png
VI.autorelease怎么加入自動釋放池中
image.png
image.png
image.png
VII.對象在自動釋放池中的出棧,出棧會調(diào)用
atautoreleasepoolobj = objc_autoreleasePoolPush();objc_autoreleasePoolPop(atautoreleasepoolobj);
理朋,壓棧找child絮识,出棧找parentimage.png
image.png
image.png
releaseUntil
image.png
kill
image.png
從子節(jié)點(diǎn)到父節(jié)點(diǎn)绿聘,不斷的出棧,kill掉初始化數(shù)據(jù)次舌,往父節(jié)點(diǎn)平移
image.png
2.Runloop
I.保持程序的持續(xù)運(yùn)行
II.處理APP中的各種事件(觸摸熄攘、定時器、performSelector)
III.節(jié)省cpu資源垃它、提供程序的性能:該做事就做事鲜屏,該休息就休息
image.png
runloop是一個do while循環(huán),和普通的循環(huán)有區(qū)別国拇,循環(huán)會持續(xù)占用cpu洛史,runloop不會持續(xù)占用cpu
image.png
runloop中的item
image.png
timer類型響應(yīng)runloop
image.png
IV.runloop是什么
通過主運(yùn)行循環(huán)查詢源碼
CFRunLoopRef mainRunloop = CFRunLoopGetMain()
image.png
image.png
線程和runloop綁定是怎么創(chuàng)建的呢?runloop是一個對象酱吝,有多個屬性
image.png
image.png
V.runloop和線程的關(guān)系
image.png
VI.runloop的事務(wù)處理原理:子線程的runloop默認(rèn)不啟動
image.png
子線程的runloop不run也殖,子線程中的timer不會執(zhí)行,timer要加到對應(yīng)模型的runloop中才能執(zhí)行务热,timer是如何加到runloop中的呢忆嗜?
image.png
存在多種類型的事務(wù),timer崎岂,observer,source,main_dispatch_queue捆毫,block(),timer中的內(nèi)容是如何回調(diào)的?
runloop中add(timer)會調(diào)用,什么時候執(zhí)行呢冲甘?在調(diào)用runloop run的時候
image.png
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())->__CFRunLoopDoTimer(rl, rlm, rlt)
image.png
image.png
timer的執(zhí)行分為四步
A.timer依賴的mode加入runloop
B.runloop run -> do timers 的時候把所有的timers執(zhí)行
C.執(zhí)行單個的timer, do timer
D.執(zhí)行回調(diào)函數(shù)
同理绩卤,source和observer是一樣的道理
image.png
image.png
VII.runloop底層原理
image.png
image.png
image.png
image.png
image.png
image.png
RunLoop在kCFRunLoopBeforeWaiting和kCFRunLoopAfterWaiting兩個狀態(tài)間進(jìn)行切換
VIII.子線程中發(fā)送通知要添加runloop,否則會無法進(jìn)入回調(diào)函數(shù),無法收到通知
@property (nonatomic, strong) NSThread *thread;
- (NSThread *)thread {
if (!_thread) {
_thread = [[NSThread alloc] initWithBlock:^{
NSRunLoop *ns_runloop = [NSRunLoop currentRunLoop];
[ns_runloop addPort:[NSPort port] forMode:NSRunLoopCommonModes];
CFRunLoopRef runloop = CFRunLoopGetCurrent();
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"進(jìn)入runLoop");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"處理timer事件");
break;
case kCFRunLoopBeforeSources:
NSLog(@"處理source事件");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"進(jìn)入睡眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"被喚醒");
break;
case kCFRunLoopExit:
NSLog(@"退出");
break;
default:
break;
}
});
CFRunLoopAddObserver(runloop, observer, kCFRunLoopCommonModes);
CFRelease(observer);
[ns_runloop run];
}];
[_thread start];
}
return _thread;
}
- (void)postNotification:(UIButton *)sender {
NSLog(@"postNotification:%@", [NSThread currentThread]);
[self performSelector:@selector(postNotification) onThread:self.thread withObject:nil waitUntilDone:YES];
}
- (void)postNotification {
NSLog(@"1");
NSLog(@"%@", [NSThread currentThread]);
//NSPostWhenIdle
//NSPostASAP
//NSPostNow
NSNotification *notification = [NSNotification notificationWithName:@"JKRNO" object:nil];
[[NSNotificationQueue defaultQueue] enqueueNotification:notification postingStyle:NSPostWhenIdle coalesceMask:NSNotificationNoCoalescing forModes:@[NSDefaultRunLoopMode]];
NSLog(@"3");
}
- (void)receiceNotification:(NSNotification *)notification {
NSLog(@"2");
NSLog(@"%@", [NSThread currentThread]);
}
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(receiceNotification:)
name:@"JKRNO"
object:nil];
NSLog(@"viewDidLoad addObserver:%@",[NSThread currentThread]);
}
//輸出
2021-08-14 12:02:40.511246+0800 NotificationCenter[24109:26743192] 被喚醒
2021-08-14 12:02:40.514713+0800 NotificationCenter[24109:26743192] 處理timer事件
2021-08-14 12:02:40.514929+0800 NotificationCenter[24109:26743192] 處理source事件
2021-08-14 12:02:40.556199+0800 NotificationCenter[24109:26743192] 處理timer事件
2021-08-14 12:02:40.556345+0800 NotificationCenter[24109:26743192] 處理source事件
2021-08-14 12:02:40.556662+0800 NotificationCenter[24109:26743192] 處理timer事件
2021-08-14 12:02:40.556754+0800 NotificationCenter[24109:26743192] 處理source事件
2021-08-14 12:02:40.556938+0800 NotificationCenter[24109:26743192] 進(jìn)入睡眠
2021-08-14 12:02:40.566032+0800 NotificationCenter[24109:26743192] 被喚醒
2021-08-14 12:02:40.566189+0800 NotificationCenter[24109:26743192] 處理timer事件
2021-08-14 12:02:40.566285+0800 NotificationCenter[24109:26743192] 處理source事件
2021-08-14 12:02:40.568599+0800 NotificationCenter[24109:26743192] postNotification:<NSThread: 0x600003c3ca00>{number = 1, name = main}
2021-08-14 12:02:50.351417+0800 NotificationCenter[24109:26743889] 被喚醒
2021-08-14 12:02:50.351604+0800 NotificationCenter[24109:26743889] 處理timer事件
2021-08-14 12:02:50.351728+0800 NotificationCenter[24109:26743889] 處理source事件
2021-08-14 12:02:50.351833+0800 NotificationCenter[24109:26743889] 1
2021-08-14 12:02:50.351997+0800 NotificationCenter[24109:26743889] <NSThread: 0x600003c7fc00>{number = 9, name = (null)}
2021-08-14 12:02:50.352976+0800 NotificationCenter[24109:26743889] 3
2021-08-14 12:02:50.353519+0800 NotificationCenter[24109:26743192] 處理timer事件
2021-08-14 12:02:50.353145+0800 NotificationCenter[24109:26743889] 退出
2021-08-14 12:02:50.353637+0800 NotificationCenter[24109:26743192] 處理source事件
2021-08-14 12:02:50.353749+0800 NotificationCenter[24109:26743192] 進(jìn)入睡眠
2021-08-14 12:02:50.353701+0800 NotificationCenter[24109:26743889] 進(jìn)入runLoop
2021-08-14 12:02:50.354506+0800 NotificationCenter[24109:26743889] 處理timer事件
2021-08-14 12:02:50.354949+0800 NotificationCenter[24109:26743889] 處理source事件
2021-08-14 12:02:50.355314+0800 NotificationCenter[24109:26743889] 進(jìn)入睡眠
2021-08-14 12:02:53.405512+0800 NotificationCenter[24109:26743889] 2
2021-08-14 12:02:53.406716+0800 NotificationCenter[24109:26743889] <NSThread: 0x600003c7fc00>{number = 9, name = (null)}
2021-08-14 12:02:53.407182+0800 NotificationCenter[24109:26743889] 被喚醒
2021-08-14 12:02:53.407860+0800 NotificationCenter[24109:26743889] 處理timer事件
2021-08-14 12:02:53.408877+0800 NotificationCenter[24109:26743889] 處理source事件
2021-08-14 12:02:53.409602+0800 NotificationCenter[24109:26743889] 進(jìn)入睡眠
runloop狀態(tài)
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0),
kCFRunLoopBeforeTimers = (1UL << 1),
kCFRunLoopBeforeSources = (1UL << 2),
kCFRunLoopBeforeWaiting = (1UL << 5),
kCFRunLoopAfterWaiting = (1UL << 6),
kCFRunLoopExit = (1UL << 7),
kCFRunLoopAllActivities = 0x0FFFFFFFU
};