序文
Runloop は、iOS のスレッドに適用されるループ メカニズムです。システム自体には実行ループを作成するための API はありませんが、現在の RunLoop は currentRunLoop を通じて取得できます。メインスレッド自体は runloop を持っており実行状態ですが、サブスレッドの runLoop を手動で開かないと入力ソースやタイミングソースを監視できません。子スレッド RunLoop は、子スレッドのイベント ソースが終了すると閉じられ、子スレッドが終了すると解放されます。
RunLoopオブジェクトの取得/作成
[NSRunLoop currentRunLoop];//当前线程的runLoop
[NSRunLoop mainRunLoop];//主线程runLoop
CFRunLoopGetCurrent();
CFRunLoopGetMain();
[NSRunLoop currentRunLoop].getCFRunLoop; //NSRunLoop转CFRunLoopRef
NSRunLoop オブジェクトはスレッド セーフではありません。同じ runLoop オブジェクトを異なるスレッドで使用する場合は、CFRunLoopRef を使用してスレッド セーフを確保できます。
タイマーと入力ソースを追加する
[[NSRunLoop currentRunLoop] addTimer:<#(nonnull NSTimer *)#> forMode:<#(nonnull NSRunLoopMode)#>]
[[NSRunLoop currentRunLoop] addPort:<#(nonnull NSPort *)#> forMode:<#(nonnull NSRunLoopMode)#>]
CFRunLoopAddTimer(<#CFRunLoopRef rl#>, <#CFRunLoopTimerRef timer#>, <#CFRunLoopMode mode#>)
CFRunLoopAddSource(<#CFRunLoopRef rl#>, <#CFRunLoopSourceRef source#>, <#CFRunLoopMode mode#>)
RunLoopを開始する
[[NSRunLoop currentRunLoop] run]; //无条件且以默认的NSDefaultRunLoopMode启动
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate new]]; //指定过期时间且以默认的NSDefaultRunLoopMode启动
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate new]];//指定过期时间,指定启动方式
CFRunLoopRun(); //子线程的runLoop需要启动
CFRunLoopRunInMode(<#CFRunLoopMode mode#>, <#CFTimeInterval seconds#>, <#Boolean returnAfterSourceHandled#>)
RunLoop を終了する
/* 给RunLoop设置超时时间 */
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate new]]; //指定过期时间且以默认的NSDefaultRunLoopMode启动
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate new]];//指定过期时间,指定启动方式
/* 通知RunLoop停止 */
CFRunLoopStop([NSRunLoop currentRunLoop].getCFRunLoop);
子スレッドの RunLoop はイベント ソースの終了とともに終了するため、通常、RunLoop はアクティブに停止されません。
メインスレッドのシナリオ
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(fireDemo) userInfo:nil repeats:YES];
タイマーは直接使用でき、デフォルトは NSDefaultRunLoopMode です。
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
UIScrollViewをスライドさせるとRunLoopはUITrackingRunLoopModeモードに切り替わりますが、NSDefaultRunLoopModeのデフォルトのTimerは呼び出されなくなりますが、NSRunLoopCommonModesに設定することで通常に戻すことができます。
サブスレッドのシナリオ
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerCount) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; //添加timer,使用默认runloop模式
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
//timer结束后,触发breakPoint
});
子スレッドは RunLoop コードを開始します。runLoop が開始されると、runMode 以降のコードは実行されません。runMode 以降のコードは、Timer が終了して RunLoop が終了するまで実行されません。タイマーの繰り返しが YES の場合、runMode 以降のコードは実行されます;子スレッド RunLoop には UITrackingRunLoopMode モードがありません。
//开启子线程runloop时必须添加相应的timer或port,否知运行无效
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
[[NSRunLoop currentRunLoop] run];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerCount) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
//timer结束后,没有触发breakPoint
});
[currentRunLoop run] メソッドを呼び出してサブスレッドの実行ループを開始すると、停止することはできません。実行がブロックされた後、コードが実行され、サブスレッドは解放されません。
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3.0]];
//do something
コードは実行後にブロックされ、runLoop が終了するまで実行されません。
常駐サブスレッドを作成する
+ (NSThread *)networkThread {
static dispatch_once_t onceToken;
static NSThread *_networkThread;
dispatch_once(&onceToken, ^{
_networkThread = [[NSThread alloc] initWithTarget:[[self class] sharedInstance] selector:@selector(_runLoopThread) object:nil];
[_networkThread setName:@"com.network.request"];
[_networkThread setQualityOfService:[[NSThread mainThread] qualityOfService]];
[_networkThread start];
});
return _networkThread;
}
- (void)_runLoopThread {
// 直接使用run比较暴力,无法手动销毁线程
[[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
/* 另一种形式
[[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
while (!_stopRunning) {
@autoreleasepool {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}
*/
}
コードはブロックするがスレッドはブロックしない
__block BOOL isContinue = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
isContinue = YES;
});
while (isContinue == NO) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
//isContinue为YES后执行后续代码
実際のプロジェクトでは、このメソッドは、スレッド操作をブロックせずにブロック コードを実装するためにメイン スレッドでよく使用されます。たとえば、非同期操作が完了するのを待ってから後続のコード ブロックを呼び出します。