多线程二: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。

猜你喜欢

转载自blog.csdn.net/M316625387/article/details/83787313