iOS中timer相關的延時調用,常見的有NSObject中的performSelector:withObject:afterDelay:這個方法在調用的時候會設置當前runloop中timer,還有一種延時,直接使用NSTimer來配置任務。xcode
這兩種方式都一個共同的前提,就是當前線程裏面須要有一個運行的runloop而且這個runloop裏面有一個timer。安全
咱們知道:只有主線程會在建立的時候默認自動運行一個runloop,而且有timer,普通的子線程是沒有這些的。這樣就帶來一個問題了,有些時候咱們並不肯定咱們的模塊是否是會異步調用到,而咱們在寫這樣的延時調用的時候通常都不會去檢查運行時的環境,這樣在子線程中被調用的時候,咱們的代碼中的延時調用的代碼就會一直等待timer的調度,可是實際上在子線程中又沒有這樣的timer,這樣咱們的代碼就永遠不會被調到。多線程
下面的代碼展現了performSelector和dispatch_time的不一樣併發
- -(void) testDispatch_after{
- dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC);
- dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- dispatch_after(time, queue, ^{
- NSLog(@"3秒後添加到隊列");
- });
- dispatch_release(queue);
- }
- -(void) testDelay{
- NSLog(@"testDelay被執行");
- }
- -(void) testDispatch_Barrier{
-
- dispatch_queue_t gcd = dispatch_queue_create("這是併發隊列", DISPATCH_QUEUE_CONCURRENT);
- dispatch_async(gcd, ^{
- NSLog(@"b0");
-
- [self performSelector:@selector(testDelay) withObject:nil afterDelay:3];
-
- [self testDispatch_after];
- });
- dispatch_release(gcd);
- }
在有多線程操做的環境中,這樣performSelector的延時調用,實際上是缺少安全性的。咱們能夠用另外一套方案來解決這個問題,就是使用GCD中的dispatch_after來實現單次的延時調用異步
解決方案一:
performSelector並非沒有辦法保證線程安全。例以下面的代碼就能夠運行:async
- [self performSelector:@selector(testDelay) onThread:[NSThread mainThread] withObject:nil waitUntilDone:NO];
指定了該selector在主線程中運行。oop
解決方案二:
- [self performSelector:@selector(testDelay) withObject:nil afterDelay:3 inModes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
- [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
啓動線程中runloop,由於每一個線程就有個默認的runloopspa
解決方案三:線程
1. [self performSelector:@selector(printLog) withObject:nil afterDelay:2.0f inModes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];code
2.[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];