在我的另一篇文章RunLoop簡單介紹了關(guān)于runLoop基礎(chǔ)知識和NSTimer時runloop簡單應(yīng)用, 下面看下怎么使用RunLoop。
一歉眷、Runloop的啟動
在談到RunLoop與線程的關(guān)系時, 每個人都會說主線程的RunLoop是默認(rèn)自動創(chuàng)建啟動的,子線程的 RunLoop 需要手動創(chuàng)建,手動啟動, 這是在多數(shù)博文里都寫到的,但是主線程的RunLoop是怎樣啟動, 又怎樣手動創(chuàng)建的呢
1. 主線程的RunLoop是默認(rèn)啟動的
-
先看下程序啟動時都做了什么,此處盜圖
下面看下main()函數(shù)
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
在main()函數(shù)里只調(diào)用一個UIApplicationMain()函數(shù), 先點進(jìn)去看下UIApplicationMain函數(shù)
1. UIKIT_EXTERN int UIApplicationMain(int argc, char * _Nonnull * _Null_unspecified argv, NSString * _Nullable principalClassName, NSString * _Nullable delegateClassName);
從上面UIApplicationMain函數(shù)看到返回值是int類型,那么開始改造下代碼,看UIApplicationMain()返回的是什么
int main(int argc, char * argv[]) {
@autoreleasepool {
// return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
// 開始改造main函數(shù)
NSLog(@"調(diào)用UIApplicationMain函數(shù)");
int who = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
NSLog(@"UIApplicationMain函數(shù)的返回值: %d", who);
return who;
}
}
運行試下, 沒有打印返回值who穿铆。可以看出UIApplicationMain
函數(shù)開啟了一個和主線程相關(guān)的RunLoop
,導(dǎo)致UIApplicationMain
不會返回褂萧,一直在運行中,也就保證了程序的持續(xù)運行葵萎。
2. 子線程開啟RunLoop
子線程的 RunLoop 需要手動創(chuàng)建,手動啟動
// 子線程 開啟 RunLoop
// 創(chuàng)建子線程
_thead = [[NSThread alloc] initWithTarget:self selector:@selector(startRunLoop) object:nil];
// 開啟 子線程
[_thead start];
- (void)startRunLoop
{
// 獲取 currentRunLoop
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
// 因為在子線程中開啟runloop 至少需要一個timer 或者 source ,runloop 才會啟動. addPort 是比添加timer 更好的方式
[runloop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
// 開啟 runloop
[runloop run];
}
注意
runloop 在線程中獲取就是創(chuàng)建
RunLoop應(yīng)用場景
下面介紹幾個RunLoop應(yīng)用場景
1. 子線程的NSTimer
在子線程中添加定時器, 必須手動創(chuàng)建并開啟RunLoop, 將Timer添加進(jìn)創(chuàng)建的runloop中, 否則定時器無效导犹。
_thead = [[NSThread alloc] initWithBlock:^{
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}];
[_thead start];
2. 滑動與圖片刷新
問題: tableview的cell中的imageView, 需要從網(wǎng)絡(luò)加載的圖片的時候,上下滑動動tableView羡忘,異步線程會去獲取圖片谎痢,獲取完成后主線程就會設(shè)置cell的圖片,但是會造成卡頓卷雕。
解決 滑動tableView的時候节猿,RunLoop是在 UITrackingRunLoopMode
下進(jìn)行,此時不會去設(shè)置圖片漫雕,所以可以設(shè)置將圖片的任務(wù)在NSDefaultRunLoopMode
下進(jìn)行滨嘱,即當(dāng)tableView滑動停止的時候,再去設(shè)置圖片浸间。
[_imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@""] afterDelay:0.5 inModes:@[NSDefaultRunLoopMode]];
3. 常駐子線程
因為在子線程中開啟runloop 至少需要一個timer 或者 source ,runloop 才會啟動太雨。所以想要保持線程長期運轉(zhuǎn),必須讓子線程一直處理事件魁蒜∧野猓可以在子線程中加入RunLoop吩翻,并且給Runloop設(shè)置item,防止Runloop自動退出宪拥。
- a. 添加Timer ---- 參考上文中 子線程的NSTimer
- b. 添加事件源 ---- 參考上文中 子線程開啟RunLoop
關(guān)于RunLoop的應(yīng)用場景還有很多, 如果感興趣的話, 推薦去看看 我的runloop學(xué)習(xí)筆記 寫得非常好, 里面都是實際項目中的使用點, 還附有Demo仿野。