RunLoop
作用
- 使程序一直運行并接收用戶的輸入
- 決定程序在何時處理哪些事件
- 調(diào)用解耦(Message Queue)
- 節(jié)省CPU時間(當程序啟動后颗管,什么都沒有執(zhí)行的話,就不用讓CPU來消耗資源來執(zhí)行鄙币,直接進入睡眠狀態(tài))
模式
RunLoop
在同一段時間只能且必須在一種特定的模式下運行如果要更換 Mode,必須先停止當前的 Loop,然后再重新啟動 Loop
Mode 是保證滾動流暢的關(guān)鍵
NSDefaultRunLoopMode
:默認狀態(tài)、空閑狀態(tài)UITrackingRunLoopMode
:滾動模式,專門為滾動視圖設計的NSRunLoopCommonModes
:默認包含以上兩種模式UIInitializationRunLoopMode
:私有的,App啟動時
模擬 RunLoop 實現(xiàn)
- 準備的方法
void callFunc(int num) {
NSLog(@"正在執(zhí)行 %d 功能...",num);
}
- main 方法中的實現(xiàn)
int main(int argc, const char * argv[]) {
@autoreleasepool {
int result = 0;
while (YES) {
printf("請輸入選項,O表示退出");
// 阻塞式 : 需要控制臺輸入值才能繼續(xù)運行
scanf("%d",&result);
if (0==result) {
NSLog(@"程序正常退出");
break;
} else {
callFunc(result);
}
}
}
return 0;
}
運行循環(huán)與時鐘
- 實際開發(fā)中,不建議將時鐘的運行模式設置成 NSRunLoopCommonModes ,一旦耗時操作會影響流暢度
- (void)viewDidLoad {
[super viewDidLoad];
// 定時器 : 添加了滾動事件之后,定時器就不再執(zhí)行了
// [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];
// timerWithTimeInterval 創(chuàng)建時鐘時,需要將定時器添加到運行循環(huán)中
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}
時鐘的執(zhí)行方法入口
- 模擬耗時操作 : 此處不能使用sleep, 因為線程休眠的時候事件循環(huán)不響應任何事件,開發(fā)中一定不要使用
- (void)fire
{
// 不能使用 sleep
// [NSThread sleepForTimeInterval:1.0];
// 耗時操作
for (int i = 0; i < 1000*1000; i++) {
NSString *str = [NSString stringWithFormat:@"hello %d",i];
}
static int num = 0;
num++;
NSLog(@"%d",num);
}
運行測試流椒,會發(fā)現(xiàn)卡頓非常嚴重
將時鐘添加到子線程中工作
/// 線程
@property (nonatomic,strong) NSThread *thread;
- (void)viewDidLoad {
[super viewDidLoad];
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
[self.thread start];
}
- 注意 : 主線程的運行循環(huán)是默認啟動的,但是子線程的運行循環(huán)是默認不工作的,這樣能夠保證線程執(zhí)行完畢后,自動被銷毀
- (void)demo
{
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
// 啟動當前線程的運行循環(huán);當前線程的運行循環(huán)是個不同于while的死循環(huán)
// [[NSRunLoop currentRunLoop] run];
CFRunLoopRun();
NSLog(@"over");
}
- 停止運行循環(huán)
// 停止當前運行循環(huán)
CFRunLoopStop(CFRunLoopGetCurrent());
按鈕點擊
創(chuàng)建按鈕
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *addBtn = [UIButton buttonWithType:UIButtonTypeContactAdd];
[self.view addSubview:addBtn];
addBtn.center = self.view.center;
// 向 runloop 注冊監(jiān)聽,點擊按鈕,執(zhí)行 ViewController 的 click 方法
[addBtn addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];
}
點擊方法
- (void)click
{
NSLog(@"%s",__FUNCTION__);
}
時間循環(huán)圖解按鈕點擊
RunLoop-主線程
- 主線程的消息循環(huán)是默認開啟.
- 在主線程中使用
定時源
.即定時器
. - 步驟 : 將
定時源
添加到當前線程
的消息循環(huán).
代碼實現(xiàn)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self timerDemo];
}
- (void)timerDemo
{
// 創(chuàng)建定時器
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];
// 將定時器添加到消息循環(huán)
// currentRunLoop : 獲取到當前的消息循環(huán)
// forMode : 當前定時源timer的運行模式
// NSRunLoopCommonModes : 模式組,里面包含了幾種運行模式,kCFRunLoopDefaultMode / UITrackingRunLoopMode
// 消息循環(huán)也是運行在一個模式下面的,默認的模式是kCFRunLoopDefaultMode,只有定時源的運行模式和消息循環(huán)的運行模式保持一致,定時源對應的方法才能執(zhí)行
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void)fire
{
NSLog(@"hello %@",[NSRunLoop currentRunLoop].currentMode);
}
RunLoop-子線程
- 子線程的消息循環(huán)是默認不開啟.
- 在子線程中使用
定時源
.即定時器
.需要我們手動開啟子線程的消息循環(huán)
. - 步驟 : 將
定時源
添加到當前線程
的消息循環(huán).
代碼實現(xiàn)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// [self timerDemo];
NSLog(@"start");
[self performSelectorInBackground:@selector(timerDemo) withObject:nil];
}
- (void)timerDemo
{
//創(chuàng)建定時器(定時源)
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];
// 獲取當前的消息循環(huán)
// currentRunLoop : 當前子線程的消息循環(huán)
// 子線程的消息循環(huán)默認是不開啟的,需要我們手動開啟
// 子線程中的運行模式和主線程是一樣的
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
// 手動開啟子線程的消息循環(huán)
// run : 消息循環(huán)不停止的話,后面的代碼永遠不會被執(zhí)行的
// runUntilDate : 消息循環(huán)運行到指定的日期之后就自動的停止
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
// 提問 : over能夠打印出來嗎?
NSLog(@"over");
}
- (void)fire
{
NSLog(@"hello");
}