亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

iOS開發(fā)runloop運(yùn)行循環(huán)機(jī)制學(xué)習(xí)

 更新時(shí)間:2022年07月21日 08:34:53   作者:滄海一笑-夢(mèng)中的瞬間  
這篇文章主要為大家介紹了iOS開發(fā)runloop運(yùn)行循環(huán)的機(jī)制學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

RunLoop:又叫運(yùn)行循環(huán)機(jī)制,在iOS中的兩大機(jī)制之一。并不是只有iOS有Runloop其他語言也有,他們的方式不太一樣,但是核心都是為了解決性能和良好的運(yùn)行,例如:webJs里Runloop也稱作eventLoop,由于js沒有多線程,在這樣的情況做了一種調(diào)用棧來配合主線程運(yùn)行。而在iOS里面runloop就不太一樣,因?yàn)橛卸嗑€程的原因,runloop是配合多線程使用的。每一個(gè)線程都對(duì)應(yīng)一個(gè)runloop。

Runloop最核心的事情就是保證程序的持續(xù)運(yùn)行讓線程在沒有消息的時(shí)候休眠,在有消息時(shí)喚醒,以提高程序性能。這個(gè)機(jī)制是依靠系統(tǒng)內(nèi)核來完成的(蘋果操作系統(tǒng)核心組件 Darwin 中的 Mach)
概念:RunLoop 是通過內(nèi)部維護(hù)的事件循環(huán)(Event Loop)來對(duì)事件/消息進(jìn)行管理的一個(gè)對(duì)象。

1、沒有消息處理時(shí),休眠已避免資源占用,由用戶態(tài)切換到內(nèi)核態(tài)(CPU-內(nèi)核態(tài)和用戶態(tài))

2、有消息需要處理時(shí),立刻被喚醒,由內(nèi)核態(tài)切換到用戶態(tài)

main函數(shù)是不會(huì)退出的,為什么呢?這個(gè)時(shí)候就是 UIApplicationMain 內(nèi)部默認(rèn)開啟了主線程的 RunLoop,并執(zhí)行了一段無限循環(huán)的代碼(不是簡(jiǎn)單的 for 循 環(huán)或 while 循環(huán))

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

 UIApplicationMain 函數(shù)一直沒有返回,而是不斷地接收處理消息以及等待休眠,所以運(yùn)行程序之后會(huì)保 持持續(xù)運(yùn)行狀態(tài)

// 偽代碼
int main(int argc, char * argv[]) {
    BOOL running = YES;
    do {
        //執(zhí)行的事件
    } while(running)
  return 0;
}

一、Runloop的實(shí)現(xiàn)機(jī)制

RunLoop 通過 mach_msg()函數(shù)接收、發(fā)送消息。它的本質(zhì)是調(diào)用函數(shù) mach_msg_trap(),相當(dāng)于是一 個(gè)系統(tǒng)調(diào)用,會(huì)觸發(fā)內(nèi)核狀態(tài)切換。

在用戶態(tài)調(diào)用 時(shí)會(huì)切換到內(nèi)核態(tài);內(nèi)核態(tài)中內(nèi)核 實(shí)現(xiàn)的 mach_msg()函數(shù)會(huì)完成實(shí)際的工作。

二、Runloop 數(shù)據(jù)結(jié)構(gòu)

是 CFRunLoop(CoreFoundation)的封裝,提供了面向?qū)ο蟮?nbsp;API 相關(guān)的主要涉及五個(gè)類:

  • CFRunLoop:Runloop對(duì)象 (由 pthread(線程對(duì)象,說明 和線程是一一對(duì)應(yīng)的)、currentMode(當(dāng)前所處的運(yùn)行模式)、 modes(多個(gè)運(yùn)行模式的集合)、 (模式名稱字符串集合)、,Timer,Source 集合)構(gòu)成)
  • CFRunLoopMode:運(yùn)行模式(由 name、source0、source1、observers、timers 構(gòu)成)
  • CFRunLoopSource:輸入源/事件源 (分為 source0 和 source1 兩種)
  • CFRunLoopTimer:定時(shí)源(基于時(shí)間的觸發(fā)器,基本上說的就是 NStimer。在預(yù)設(shè)的時(shí)間點(diǎn)喚醒 執(zhí)行回調(diào)。因?yàn)樗腔?nbsp;RunLoop 的,因此它不是實(shí)時(shí)的(就是 NStimer 是不準(zhǔn)確的。 因?yàn)橹回?fù)責(zé)分發(fā)源的消息。如果 線程當(dāng)前正在處理繁重的任務(wù),就有可能導(dǎo)致 Timer 本次延時(shí),或者少執(zhí)行一次))
  • CFRunLoopObserver:觀察者(對(duì)相關(guān)事件runloop的狀態(tài)進(jìn)行監(jiān)聽)

CFRunLoopSource分為兩種source0和source1詳解:

  • source0:
    即非基于 port 的,也就是用戶觸發(fā)的事件。需要手動(dòng)喚醒線程,將當(dāng)前線程從內(nèi)核態(tài)切換到用戶 態(tài)
  • source1:
    基于 port 的,包含一個(gè)mach_port和一個(gè)回調(diào),可監(jiān)聽系統(tǒng)端口和通過內(nèi)核和其他線程發(fā)送的消息能主動(dòng)喚醒Runloop接受分發(fā)系統(tǒng)事件 具備喚醒事件的能力

CFRunLoopObserver監(jiān)聽時(shí)間點(diǎn)詳細(xì)事件

  • kCFRunLoopEntry RunLoop 準(zhǔn)備啟動(dòng)
  • kCFRunLoopBeforeTimers RunLoop 將要處理一些 Timer 相關(guān)事件
  • kCFRunLoopBeforeSources RunLoop 將要處理一些 Source 事件
  • kCFRunLoopBeforeWaiting RunLoop 將要進(jìn)行休眠狀態(tài),即將由用戶態(tài)切換到內(nèi)核態(tài)
  • kCFRunLoopAfterWaiting RunLoop 被喚醒,即從內(nèi)核態(tài)切換到用戶態(tài)后
  • kCFRunLoopExit RunLoop 退出
  • kCFRunLoopAllActivities 監(jiān)聽所有狀態(tài)

線程和 RunLoop 一一對(duì)應(yīng), RunLoop 和 Mode 是一對(duì)多的,Mode 和 source、timer、observer 也是一對(duì)多 的

三、實(shí)現(xiàn)機(jī)制

Runloop運(yùn)行的大致邏輯是:

通知觀察者 RunLoop 即將啟動(dòng)。

通知觀察者即將要處理 Timer 事件。

通知觀察者即將要處理 source0 事件。

處理 source0 事件。

如果基于端口的源(Source1)準(zhǔn)備好并處于等待狀態(tài),進(jìn)入步驟 9。

通知觀察者線程即將進(jìn)入休眠狀態(tài)。

將線程置于休眠狀態(tài),由用戶態(tài)切換到內(nèi)核態(tài),直到下面的任一事件發(fā)生才喚醒線程。

  • 一個(gè)基于 port 的 Source1 的事件。
  • 一個(gè) Timer 到時(shí)間了。
  • RunLoop 自身的超時(shí)時(shí)間到了。
  • 被其他調(diào)用者手動(dòng)喚醒。

通知觀察者線程將被喚醒。

處理喚醒時(shí)收到的事件

  • 如果用戶定義的定時(shí)器啟動(dòng),處理定時(shí)器事件并重啟 RunLoop。進(jìn)入步驟 2。
  • 如果輸入源啟動(dòng),傳遞相應(yīng)的消息。
  • 如果 RunLoop 被顯示喚醒而且時(shí)間還沒超時(shí),重啟 RunLoop。進(jìn)入步驟 2

通知觀察者 RunLoop 結(jié)束。

觀察者observer 怎么監(jiān)聽Runloop,監(jiān)聽的狀態(tài)

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    // 即將進(jìn)入runloop
    kCFRunLoopEntry = (1UL << 0),
    // 即將處理timer
    kCFRunLoopBeforeTimers = (1UL << 1),
    // 即將處理source
    kCFRunLoopBeforeSources = (1UL << 2),
    // 即將進(jìn)入休眠
    kCFRunLoopBeforeWaiting = (1UL << 5),
    // 休眠后喚醒
    kCFRunLoopAfterWaiting = (1UL << 6),
    // 退出runloop
    kCFRunLoopExit = (1UL << 7),
    // runloop所有活動(dòng)
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

四、runloop 和 線程

1、怎么創(chuàng)建一個(gè)常駐線程?

  • 為當(dāng)前線程開啟一個(gè) RunLoop(第一次調(diào)用 [NSRunLoop currentRunLoop]方法時(shí)實(shí)際是會(huì)先去創(chuàng)建一 個(gè) RunLoop)
  • 向當(dāng)前 RunLoop 中添加一個(gè) Port/Source 等維持 RunLoop 的事件循環(huán)(如果 RunLoop 的 mode 中一個(gè) item 都沒有, runloop會(huì)退出)
  • 啟動(dòng)該RunLoop
    NSRunLoop *runloop = [NSRunLoop currentRunLoop];
    [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    [runloop run];

2、如果我們開辟一個(gè)新的線程 加入了定時(shí)器的 這個(gè)時(shí)候定時(shí)器是不會(huì)執(zhí)行的,我們看下下面的代碼

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLogMeth(@"1")
    ygweakify(self);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        ygstrongify(self);
        NSLogMeth(@"2")
        [self performSelector: @selector(test) afterDelay:10];
        NSLogMeth(@"3")
    });
    NSLogMeth(@"4")
}
- (void)test {
    NSLogMeth(@"5")
}

答案是 1423,test 方法并不會(huì)執(zhí)行。

原因是如果是帶 afterDelay 的延時(shí)函數(shù),會(huì)在內(nèi)部創(chuàng)建一個(gè) NSTimer,然后添加到當(dāng)前線程的 RunLoop 中。 也就是如果當(dāng)前線程沒有開啟 RunLoop,該方法會(huì)失效。

我們?cè)倏戳硪粋€(gè)

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        ygstrongify(self);
        NSLogMeth(@"2")
        [NSRunLoop currentRunLoop] run];
        [self performSelector: @selector(test) afterDelay:10];
        NSLogMeth(@"3")
    });
    NSLogMeth(@"4")
}

答案依然是 1423,test 方法并不會(huì)執(zhí)行。

原因是如果 RunLoop 的 mode 中一個(gè) item 都沒有,RunLoop 會(huì)退出。即在調(diào)用 RunLoop 的 run 方法后,由 于其 mode 中沒有添加任何 item 去維持 RunLoop 的時(shí)間循環(huán),RunLoop 隨即還是會(huì)退出。 所以我們自己?jiǎn)?dòng) RunLoop,一定要在添加 item 后 所以我們把 開啟runloop的代碼 放在 延時(shí)方法之后 就好了

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        ygstrongify(self);
        NSLogMeth(@"2")
        [self performSelector: @selector(test) afterDelay:10];
        [NSRunLoop currentRunLoop] run];
        NSLogMeth(@"3")
    });
    NSLogMeth(@"4")
}

這個(gè)時(shí)候test的方法就執(zhí)行了

3、怎樣保證子線程數(shù)據(jù)回來更新 UI 的時(shí)候不打斷用戶的滑動(dòng)操作?

當(dāng)我們?cè)谧诱?qǐng)求數(shù)據(jù)的同時(shí)滑動(dòng)瀏覽當(dāng)前頁面,如果數(shù)據(jù)請(qǐng)求成功要切回主線程更新 UI,那么就會(huì)影響當(dāng) 前正在滑動(dòng)的體驗(yàn)。
我們就可以將更新 UI 事件放在主線程的 上執(zhí)行即可,這樣就會(huì)等用戶不再滑動(dòng)頁 面,主線程 RunLoop 由 切換到 時(shí)再去更新 UI

[self performSelectorOnMainThread: @selector(readload) withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];

4、NSTimer 在runloop中的關(guān)系

NSTimer其實(shí)就是 CFRunLoopTimerRef(基于時(shí)間的觸發(fā)器) ,他們之間是tool-free bridged 的。一個(gè) NSTimer 注冊(cè)到RunLoop后, RunLoop會(huì)為其重復(fù)的時(shí)間點(diǎn)注冊(cè)好事件。例如 10:00, 10:10, 10:20 這幾個(gè)時(shí)間點(diǎn)。 RunLoop為了節(jié)省資源,并不會(huì)在非常準(zhǔn)確的時(shí)間點(diǎn)回調(diào)這個(gè) Timer。Timer 有個(gè)屬性叫做Tolerance(寬容度),標(biāo)示了當(dāng)時(shí)間點(diǎn)到后,容許有多少最大誤差。

如果某個(gè)時(shí)間點(diǎn)被錯(cuò)過了,例如執(zhí)行了一個(gè)很長的任務(wù),則那個(gè)時(shí)間點(diǎn)的回調(diào)也會(huì)跳過去,不會(huì)延后執(zhí)行。就比如等公交,如果 10:10 時(shí)我忙著玩手機(jī)錯(cuò)過了那個(gè)點(diǎn)的公交,那我只能等 10:20 這一趟了。

CADisplayLink 是一個(gè)和屏幕刷新率一致的定時(shí)器(但實(shí)際實(shí)現(xiàn)原理更復(fù)雜,和 NSTimer 并不一樣, 其內(nèi)部實(shí)際是操作了一個(gè) Source)。如果在兩次屏幕刷新之間執(zhí)行了一個(gè)長任務(wù),那其中就會(huì)有一幀被 跳過去(和NSTimer相似),造成界面卡頓的感覺。在快速滑動(dòng) TableView 時(shí),即使一幀的卡頓也會(huì) 讓用戶有所察覺。 FaceBook開源的 AsyncDisplayLink 就是為了解決界面卡頓的問題,其內(nèi)部也用 到了 RunLoop

五、異步繪制

異步繪制,就是可以在子線程把需要繪制的圖形,提前在子線程處理好。將準(zhǔn)備好圖像數(shù)據(jù)直接返給主線程使用,這樣可以降低主線程的壓力。(一般情況下我們都是在主線程繪制的大家可以作為了解,特殊情況下在處理研究)

異步繪制的過程:

要通過系統(tǒng)的 [view.delegate displayLayer:] 這個(gè)入口來實(shí)現(xiàn)異步繪制。

  • 代理負(fù)責(zé)生成對(duì)應(yīng)的 Bitmap
  • 設(shè)置該 Bitmap 為 layer.contents 屬性的值。

以上就是iOS開發(fā)runloop運(yùn)行循環(huán)機(jī)制學(xué)習(xí)的詳細(xì)內(nèi)容,更多關(guān)于iOS runloop運(yùn)行循環(huán)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • MySQL通用表空間的幾個(gè)選項(xiàng)使用指南

    MySQL通用表空間的幾個(gè)選項(xiàng)使用指南

    在?MySQL?數(shù)據(jù)庫中有效管理存儲(chǔ)和性能至關(guān)重要,通用表空間為實(shí)現(xiàn)這一目標(biāo)提供了靈活性,本文討論通用表空間并探討其功能、優(yōu)點(diǎn)和實(shí)際用法,并附有說明性示例
    2024-01-01
  • 在MySQL中使用LIMIT進(jìn)行分頁的方法

    在MySQL中使用LIMIT進(jìn)行分頁的方法

    這篇文章主要介紹了在MySQL中使用LIMIT進(jìn)行分頁的方法,作者列舉出了三種方法,并且針對(duì)跳頁等常見問題做出了提示,需要的朋友可以參考下
    2015-05-05
  • MySQL的WHERE語句中BETWEEN與IN的使用教程

    MySQL的WHERE語句中BETWEEN與IN的使用教程

    這篇文章主要介紹了MySQL的WHERE語句中BETWEEN與IN的使用教程,是MySQL入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-12-12
  • mysql索引失效的幾種情況分析

    mysql索引失效的幾種情況分析

    這篇文章主要給大家介紹了關(guān)于mysql索引失效的情況,文中通過圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Mysql5.7服務(wù)無法啟動(dòng)的圖文解決教程

    Mysql5.7服務(wù)無法啟動(dòng)的圖文解決教程

    這篇文章主要介紹了Mysql5.7服務(wù)無法啟動(dòng)問題,解決辦法非常簡(jiǎn)單,需要的的朋友參考下
    2017-02-02
  • mysql 編碼設(shè)置命令

    mysql 編碼設(shè)置命令

    mysql 編碼設(shè)置命令,需要的朋友可以參考下。
    2009-10-10
  • MySQL如何快速導(dǎo)入數(shù)據(jù)

    MySQL如何快速導(dǎo)入數(shù)據(jù)

    這篇文章主要介紹了MySQL如何快速導(dǎo)入數(shù)據(jù),幫助大家更好的理解和學(xué)習(xí)MySQL,感興趣的朋友可以了解下
    2020-08-08
  • Mysql中文亂碼以及導(dǎo)出為sql語句和Excel問題解決方法[圖文]

    Mysql中文亂碼以及導(dǎo)出為sql語句和Excel問題解決方法[圖文]

    這幾天基于Heritrix寫了一個(gè)爬蟲,用到mysql,在導(dǎo)入導(dǎo)出數(shù)據(jù)時(shí),遇到一些亂碼問題,好不容易解決了,記錄一下,以備查看
    2013-04-04
  • MySQL保證數(shù)據(jù)不丟失的方案詳解

    MySQL保證數(shù)據(jù)不丟失的方案詳解

    MySQL作為一個(gè)存儲(chǔ)數(shù)據(jù)的產(chǎn)品,怎么確保數(shù)據(jù)的持久性和不丟失才是最重要的,感興趣的可以跟隨本文一探究竟,文中通過圖文結(jié)合給大家講解的非常詳細(xì),需要的朋友快來跟著小編一起來學(xué)習(xí)吧
    2023-12-12
  • 淺談innodb_autoinc_lock_mode的表現(xiàn)形式和選值參考方法

    淺談innodb_autoinc_lock_mode的表現(xiàn)形式和選值參考方法

    下面小編就為大家?guī)硪黄獪\談innodb_autoinc_lock_mode的表現(xiàn)形式和選值參考方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-03-03

最新評(píng)論