1、执行以下代码打印结果是什么?

  1. - (void)interview01 {
  2. dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
  3. dispatch_async(queue, ^{
  4. NSLog(@"1");
  5. [self performSelector:@selector(print) withObject:nil afterDelay:3];
  6. NSLog(@"3");
  7. });
  8. }
  9. - (void)print {
  10. NSLog(@"2");
  11. }

打印结果:

  1. ~: 1
  2. ~: 3

原因分析:
performSelector:withObject:afterDelay:方法底层是由定时器实现,而定时器又依赖于RunLoop,子线程的RunLoop默认没有开启,所以这段代码不会打印2。
可以参考 GUNStep 来查看performSelector:withObject:afterDelay:实现逻辑,Foundation-RunLoop.m

如何能够让2打印:
方法一:开启当前线程的RunLoop

  1. - (void)test1 {
  2. dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
  3. dispatch_async(queue, ^{
  4. NSLog(@"1");
  5. [self performSelector:@selector(test2) withObject:nil afterDelay:3];
  6. NSLog(@"3");
  7. // 开启RunLoop
  8. NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
  9. [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
  10. });
  11. }

方法二:使用performSelector:方法调用print

  1. - (void)test4 {
  2. dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
  3. dispatch_async(queue, ^{
  4. NSLog(@"1");
  5. [self performSelector:@selector(test2)];
  6. NSLog(@"3");
  7. });
  8. }

原因是performSelector底层原理是消息发送,不受RunLoop的影响。

  1. // obcj源码 - NSObject.mm
  2. + (id)performSelector:(SEL)sel {
  3. if (!sel) [self doesNotRecognizeSelector:sel];
  4. return ((id(*)(id, SEL))objc_msgSend)((id)self, sel);
  5. }

2、执行以下代码打印结果是什么?

  1. - (void)interview02 {
  2. NSThread *thread = [[NSThread alloc] initWithBlock:^{
  3. NSLog(@"1");
  4. }];
  5. [thread start];
  6. [self performSelector:@selector(print) onThread:thread withObject:nil waitUntilDone:YES];
  7. }
  8. - (void)print {
  9. NSLog(@"2");
  10. }

打印结果:

  1. ~: 1

报错: ! target thread exited while waiting for the perform
分析:
thread在执行start方法时,会调用block内部代码,打印1。
thread没有开启RunLoop所以在start方法后会退出,所以无法在线程里继续执行任务。