1. 程式人生 > >多執行緒二:performSelector:withObject:afterDelay:方法

多執行緒二:performSelector:withObject:afterDelay:方法

面試題:
在這裡插入圖片描述
在這裡插入圖片描述

列印結果是:1、3
原因
performSelector:withObject:afterDelay:的本質是往Runloop中新增定時器
子執行緒預設沒有啟動Runloop

一、方法的含義

    [self performSelector:@selector(test) withObject:nil afterDelay:3.0];
  • 含義:三秒以後,呼叫 self的 test 方法
  • afterDelay 後面 可以寫 “.0” :表示 0秒以後執行 self的 test 方法

二、performSelector:withObject:afterDelay: 方法

  1. GCD 中寫 performSelector:withObject:afterDelay: 方法
 dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_async(queue, ^{
        NSLog(@"1");
        [self performSelector:@selector(test) withObject:nil afterDelay:0.0];
        NSLog(@"3");
    });

列印結果:
1
3

test 沒有 被呼叫。

  1. 在主執行緒中 呼叫此方法
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"1");
    [self performSelector:@selector(test) withObject:nil afterDelay:0.3];
    NSLog(@"3");
}

列印結果:
1
3
2

  1. performSelector:withObject:afterDelay: 的底層
  • 不在 NSObject 中,而是在NSRunLoop 類中
  • 帶有 afterDelay 的方法,都是在 NSRunLoop 類中定義的
  1. 為何在 主執行緒就可以呼叫 test 方法,在GCD 中卻不能呼叫 test 方法?
  • 這是因為 performSelector:withObject:afterDelay: 類的底層呼叫了 NSTimer 定時器。
  • 定時器是要新增到 runloop 中去的。
  • 這句話的底層就是 往 runloop 中添加了一個定時器。
  • 主執行緒 預設有 runloop,所以 在主執行緒 可以呼叫 test方法。
  • 而 GCD 中沒有 runloop ,所以 不會呼叫 test 方法。如果想要在 GCD 中呼叫 test 方法,需要自己開啟 runloop 。

完整程式碼:
在這裡插入圖片描述
列印結果:
1
3
2


三、performSelector:withObject: 方法

  1. 在 GCD 中呼叫 此方法
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_async(queue, ^{
        NSLog(@"1");
        [self performSelector:@selector(test) withObject:nil];
        NSLog(@"3");
    });
    

列印結果:
1
2
3

  1. performSelector:withObject: 的底層程式碼
    在這裡插入圖片描述
  • performSelector:withObject: 是在 NSObject 中定義的;
  • [self performSelector:@selector(test) withObject:nil] 會在底層轉換為 objcMsgSend(self, @selector(test)).

四、GNUstep

五、瞭解 GNUstep 中 performSelector:withObject:afterDelay: 的底層

  • base專案 -> source 資料夾 -> Foundation 資料夾 -> NSRunLoop.m

  • 找到 performSelector:withObject:afterDelay: 方法
    在這裡插入圖片描述

    • 建立一個 runloop
    • 建立一個 time
    • 把 time 新增到 runloop 中
  • initWithSelector: target: argument: delay: 方法
    在這裡插入圖片描述

這裡面 只有建立 runloop ,沒有啟動runloop ,所以需要自己啟動runloop。