iOS多線程之RunLoop

一、什么是RunLoop
  • 基本作用:

  • 保持程序的持續(xù)運行蒿柳;

  • 處理App中的各種事件(比如觸摸事件饶套、定時器事件、Selector事件)

  • 節(jié)省CPU資源垒探,提高程序性能:該做事時候做事妓蛮,該休息時候休息。

  • main函數(shù)中的RunLoop:

  • UIApplicationMain函數(shù)內部就啟動了一個RunLoop圾叼;

  • 所以UIApplicationMain函數(shù)一直沒有返回蛤克,保持了程序的持續(xù)運行扔仓;

  • 這個默認啟動的RunLoop是跟主線程相關聯(lián)的。

  • RunLoop對象

  • iOS中有2套API來訪問和使用RunLoop

    • Foundation中:NSRunLoop咖耘。
    • Core Foundation中:CFRunLoopRef。
  • NSRunLoop和CFRunLoopRef都代表著RunLoop對象撬码;

  • NSRunLoop是基于CFRunLoopRef的一層OC包裝儿倒,所以要了解RunLoop內部結構,需要多研究CFRunLoopRef層面的API(Core Foundation層面)

  • RunLoop與線程:

  • 每條線程都有唯一的一個與之對應的RunLoop對象呜笑;

  • 主線程的RunLoop已經(jīng)自動創(chuàng)建好了夫否,子線程的RunLoop需要主動創(chuàng)建;

  • RunLoop在第一次獲取時創(chuàng)建叫胁,在線程結束時銷毀凰慈。

  • 獲取RunLoop對象:

  • Foundation:

[NSRunLoop currentRunLoop] //獲得當前線程的NSRunLoop對象
[NSRunLoop mainRunLoop]    //獲得主線程的NSRunLoop對象
  • Core Foundation
CFRunLoopGetCurrent()  //獲得當前線程的RunLoop對象
CFRunLoopGetMain() //獲得主線程的RunLoop對象

二、RunLoop相關類:

  • Core Foundation中關于RunLoop的5個類:

  • CFRunLoopRef

  • CFRunLoopModeRef

  • CFRunLoopSourceRef

  • CFRunLoopTimerRef

  • CFRunLoopObserverRef


    RunLoop.png
  • CFRunLoopModeRef:

  • CFRunLoopModeRef代表RunLoop的運行模式驼鹅;

  • 一個RunLoop包含若干個Mode微谓,每個Mode又包含若干個Source/Timer/Observer;

  • 每次RunLoop啟動時,只能指定其中一個Mode输钩,這個Mode被稱作CurrentMode豺型;

  • 如果需要切換Mode,只能退出Loop买乃,再重新指定一個Mode進入姻氨。

  • 這樣做主要是為了分割開不同組的Source/Timer/Observer,讓其互不影響剪验。

  • 系統(tǒng)默認注冊了5個Mode:

    • kCFRunLoopDefaultMode:App默認的Mode,通常主線程是在這個Mode下運行肴焊;
    • UITrackingRunLoopMode:界面跟蹤Mode,用于ScrollView追蹤觸摸滑動功戚,保證界面滑動時不受其他Mode影響
    • UIInitializationRunLoopMode:在剛啟動App時進入的第一個Mode娶眷,啟動完成后就不再使用
    • GSEventReceiveRunLoopMode:接受系統(tǒng)事件的內部Mode,通常用不到
    • kCFRunLoopCommonModes:這是一個占位用的Mode疫铜,不是一種真正的Mode
  • CFRunLoopTimerRef:

  • CFRunLoopTimerRef是基于時間的觸發(fā)器茂浮;

  • 基本上說就是NSTimer,它受RunLoop的Mode影響壳咕;

  • GCD的定時器不受RunLoop的Mode影響席揽。

//調用scheduledTimer返回的定時器,已經(jīng)自動被添加到當前的runloop中谓厘,而且模式為NSDefaultRunLoopMode幌羞,一旦RunLoop進入其他模式,這個定時器就不會工作竟稳∈翳耄可以通過返回值進行模式修改
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
//以下兩行代碼和上一句代碼效果相同熊痴,當發(fā)生拖拽行為時,就停止調用run方法
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
//只有在拖拽情況下才調用run方法聂宾,定時器只在UITrackingRunLoopMode模式下工作
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
//定時器跑在標記為CommonModes的模式下果善,CommonModes包含:UITrackingRunLoopMode、kCFRunLoopDefaultMode
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
  • CFRunLoopSourceRef:一般都由系統(tǒng)確定系谐,了解

  • CFRunLoopSourceRef是事件源(輸入源)

  • 按照官方文檔巾陕,Source的分類

    • Port-Based Sources
    • Custom Input Source
    • Cocoa Perform Selector Sources
  • 按照函數(shù)調用棧,Source的分類:

    • Source0:非基于Port的
    • Source1:基于Port的纪他,通過內核和其他線程通信鄙煤,接受、分發(fā)系統(tǒng)事件
  • CFRunLoopObserverRef:

  • CFRunLoopObserverRef是觀察者茶袒,能夠監(jiān)聽RunLoop的狀態(tài)改變梯刚;

  • 可以監(jiān)聽的時間點有以下幾個:

/* 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
};
//創(chuàng)建observer
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
    NSLog(@"observer:監(jiān)聽到RunLoop狀態(tài)發(fā)生改變-----%lu",activity);
});
//添加觀察者:監(jiān)聽RunLoop的狀態(tài)
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode
//釋放observer
CFRelease(observer);
  • CF的內存管理(Core Foundation)
    1. 凡是帶有Create、Copy薪寓、Retain等字眼的函數(shù)亡资,創(chuàng)建出來的對象,都需要在最后做一次release操作向叉,比如:CFStringCreate沟于、CFURLCopy等;
    2. release函數(shù):CFRelease(對象)植康。

三旷太、RunLoop處理邏輯

  • RunLoop處理邏輯官方版


    RunLoop處理邏輯官方版.png
  • 首先判斷Mode是否為空,如果不為空就按照下圖的邏輯執(zhí)行:


    RunLoop處理邏輯圖片.png

四销睁、RunLoop應用

  • NSTimer

  • ImageView顯示

[self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"place"] afterDelay:2.0 inModes:@[NSRunLoopCommonModes]];
  • PerformSelector

  • 常駐線程

  • 在線程里面添加runloop(保證線程不死thread->runloop->mode->source(performSelector))

[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
  • 在子線程保留常駐線程定時做一些操作
    • 在主線程創(chuàng)建timer供璧,會自動將timer添加主runloop;
    • 方法一:
-(void)viewDidLoad {
        [super viewDidLoad];
        _thread = [[NSThread alloc] initWithTarget:self selector:@selector(excute) object:nil];
        [_thread start];
}
-(void)excute
{
        //首先創(chuàng)建一個timer冻记,然后將timer添加到runloop睡毒,再啟動runloop
        NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] run];
}
  • 方法二:通過調用scheduledTimerWithTimeInterval方法,會自動將timer添加到當前runloop中冗栗,然后再啟動runloop即可:
-(void)viewDidLoad {
        [super viewDidLoad];
        _thread = [[NSThread alloc] initWithTarget:self selector:@selector(excute) object:nil];
        [_thread start];
}
-(void)excute
{
        [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] run];
}
  • 自動釋放池
  • 將一些對象放到釋放池演顾,等釋放池清理時會讓池子里面所有的對象調一下release方法;
  • 在beforeWait(休眠)之前釋放池子隅居。

RunLoop常見問題

  • 什么是RunLoop钠至?

  • 從字面意思看:運行循環(huán)、跑圈胎源;

  • 其實它內部就是do-while循環(huán)棉钧,在這個循環(huán)內部不斷地處理各種任務(比如Source、Timer涕蚤、Observer)宪卿;

  • 一個線程對應一個RunLoop的诵,主線程的RunLoop默認已經(jīng)啟動,子線程的RunLoop得手動啟動(調用run方法)佑钾;

  • RunLoop只能選擇一個Mode啟動西疤,如果當前Mode中沒有任何Source、Timer休溶、Observer瘪阁,那么就直接退出RunLoop。

  • 你在開發(fā)過程中怎么使用RunLoop邮偎?什么應用場景?

  • 開啟一個常駐線程(讓一個子線程不進入消亡狀態(tài)义黎,等待其他線程發(fā)來消息禾进,處理其他時間):

    • 在子線程中開啟一個定時器;
    • 在子線程中進行一些長期監(jiān)控廉涕。
  • 可以控制定時器在特定模式下執(zhí)行泻云;

  • 可以讓某些事件(行為、任務)在特定模式下執(zhí)行狐蜕;

  • 可以添加Observer監(jiān)聽RunLoop的狀態(tài)宠纯,比如監(jiān)聽點擊事件的處理(在所有點擊事件之前做一些事情);

  • 還可以自定義源层释。

  • 自動釋放池是什么婆瓜?

  • 在RunLoop睡眠之前釋放(kCFRunLoopBeforeWaiting)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市贡羔,隨后出現(xiàn)的幾起案子廉白,更是在濱河造成了極大的恐慌,老刑警劉巖乖寒,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猴蹂,死亡現(xiàn)場離奇詭異,居然都是意外死亡楣嘁,警方通過查閱死者的電腦和手機磅轻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逐虚,“玉大人聋溜,你說我怎么就攤上這事“劝” “怎么了勤婚?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長涤伐。 經(jīng)常有香客問我馒胆,道長缨称,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任祝迂,我火速辦了婚禮睦尽,結果婚禮上,老公的妹妹穿的比我還像新娘型雳。我一直安慰自己当凡,他們只是感情好,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布纠俭。 她就那樣靜靜地躺著沿量,像睡著了一般。 火紅的嫁衣襯著肌膚如雪冤荆。 梳的紋絲不亂的頭發(fā)上朴则,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機與錄音钓简,去河邊找鬼乌妒。 笑死,一個胖子當著我的面吹牛外邓,可吹牛的內容都是我干的撤蚊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼损话,長吁一口氣:“原來是場噩夢啊……” “哼侦啸!你這毒婦竟也來了?” 一聲冷哼從身側響起丧枪,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤匹中,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后豪诲,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體顶捷,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年屎篱,在試婚紗的時候發(fā)現(xiàn)自己被綠了服赎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡交播,死狀恐怖重虑,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情秦士,我是刑警寧澤缺厉,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響提针,放射性物質發(fā)生泄漏命爬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一辐脖、第九天 我趴在偏房一處隱蔽的房頂上張望饲宛。 院中可真熱鬧,春花似錦嗜价、人聲如沸艇抠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽家淤。三九已至,卻和暖如春瑟由,著一層夾襖步出監(jiān)牢的瞬間絮重,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工错妖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人疚沐。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓暂氯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親亮蛔。 傳聞我的和親對象是個殘疾皇子痴施,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內容