面试题:
打印结果是: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: 方法
- 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 没有 被调用。
- 在主线程中 调用此方法
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1");
[self performSelector:@selector(test) withObject:nil afterDelay:0.3];
NSLog(@"3");
}
打印结果:
1
3
2
- performSelector:withObject:afterDelay: 的底层
- 不在 NSObject 中,而是在NSRunLoop 类中
- 带有 afterDelay 的方法,都是在 NSRunLoop 类中定义的
- 为何在 主线程就可以调用 test 方法,在GCD 中却不能调用 test 方法?
- 这是因为 performSelector:withObject:afterDelay: 类的底层调用了 NSTimer 定时器。
- 定时器是要添加到 runloop 中去的。
- 这句话的底层就是 往 runloop 中添加了一个定时器。
- 主线程 默认有 runloop,所以 在主线程 可以调用 test方法。
- 而 GCD 中没有 runloop ,所以 不会调用 test 方法。如果想要在 GCD 中调用 test 方法,需要自己开启 runloop 。
完整代码:
打印结果:
1
3
2
三、performSelector:withObject: 方法
- 在 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
- performSelector:withObject: 的底层代码
- performSelector:withObject: 是在 NSObject 中定义的;
[self performSelector:@selector(test) withObject:nil]
会在底层转换为objcMsgSend(self, @selector(test))
.
四、GNUstep
- 因为 runloop 的底层看不到,所以可以 看第三方写的OC 库。这就是 GNUstep
源码址: - http://www.gnustep.org/resources/downloads.php
五、了解 GNUstep 中 performSelector:withObject:afterDelay: 的底层
-
base项目 -> source 文件夹 -> Foundation 文件夹 -> NSRunLoop.m
-
找到 performSelector:withObject:afterDelay: 方法
- 创建一个 runloop
- 创建一个 time
- 把 time 添加到 runloop 中
-
initWithSelector: target: argument: delay: 方法
这里面 只有创建 runloop ,没有启动runloop ,所以需要自己启动runloop。