iOS-底層原理34-內(nèi)存管理(下)和RunLoop

《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絮识,出棧找parent
image.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
};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末江醇,一起剝皮案震驚了整個濱河市濒憋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌陶夜,老刑警劉巖凛驮,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異条辟,居然都是意外死亡黔夭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門羽嫡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纠修,“玉大人,你說我怎么就攤上這事厂僧】鄄荩” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長辰妙。 經(jīng)常有香客問我鹰祸,道長,這世上最難降的妖魔是什么密浑? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任蛙婴,我火速辦了婚禮,結(jié)果婚禮上尔破,老公的妹妹穿的比我還像新娘街图。我一直安慰自己,他們只是感情好懒构,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布焊傅。 她就那樣靜靜地躺著武福,像睡著了一般勋锤。 火紅的嫁衣襯著肌膚如雪遥缕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天秩霍,我揣著相機(jī)與錄音篙悯,去河邊找鬼。 笑死铃绒,一個胖子當(dāng)著我的面吹牛鸽照,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播颠悬,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼移宅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了椿疗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤糠悼,失蹤者是張志新(化名)和其女友劉穎届榄,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體倔喂,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铝条,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了席噩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片班缰。...
    茶點(diǎn)故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖悼枢,靈堂內(nèi)的尸體忽然破棺而出埠忘,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布莹妒,位于F島的核電站名船,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏旨怠。R本人自食惡果不足惜渠驼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鉴腻。 院中可真熱鬧迷扇,春花似錦、人聲如沸爽哎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽倦青。三九已至瓮床,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間产镐,已是汗流浹背隘庄。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留癣亚,地道東北人丑掺。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像述雾,于是被迫代替她去往敵國和親街州。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容