首先来段代码
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self performSelector:@selector(test:) withObject:@"aaa"];
[self performSelector:@selector(test:) withObject:@"bbb" afterDelay:3];
});
}
- (void)test:(NSString *)object
{
NSLog(@"======>:%@",object);
}
上面代码会输出什么呢?来跑一下看看:
======>:aaa
只会输出aaa,说明 [self performSelector:@selector(test:) withObject:@"bbb" afterDelay:3];
并没有在延迟3s后执行test方法。下面看下为什么会这样。
performSelector && performSelector:withObject:afterDelay:区别
-
performSelector
截屏2020-04-18下午3.26.07.png
我们看下performSelector
的定义:
看到performSelector方法是在NSObject类中定义的,接下来再看看加上afterDelay的performSelector方法定义 -
performSelector:withObject:afterDelay:
截屏2020-04-18下午3.27.09.png
可以看到是在NSRunloop中定义的,下面说一下具体的区别
-
区别:
1.performSelector
实际是为了方便使用objc_msgSend
的一个简单的封装,等同于:
((void (*) (id, SEL, NSString *)) objc_msgSend)(self, NSSelectorFromString(@"test:"), @"aaa");
performSelector默认最多只可传递两个参数,若需多参参考以下方式:
一种是使用NSInvocation,利用了runtime的反射机制,效率较低,可读性不高;
第二种是将参数封装进NSArray、NSDictionary等对象,可读性强,效率高;
第三种是使用objc_msgSend重写performSelector。
而且他的方法执行所在的线程跟调用他的时候所在的线程是一致的
performSelector:withObject:afterDelay:
拿上面的代码
[self performSelector:@selector(test:) withObject:@"bbb" afterDelay:3];
该方法将延迟3秒后再执行test方法。说到对时间方面的处理在项目中经常用到的是NSTimer:当一个NSTimer注册到Runloop后,Runloop会重复的在相应的时间点注册事件,当然Runloop为了节省资源并不会在准确的时间点触发事件。
而performSelector:withObject:afterDelay:
其实就是在内部创建了一个NSTimer,然后会添加到当前线程的Runloop中,所以当该方法添加到子线程中的时候需要注意一点:子线程中的runloop默认是没有启动的状态。所以我们要让当前子线程的runloop跑起来:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[NSRunLoop currentRunLoop] run];
[self performSelector:@selector(test:) withObject:@"bbb" afterDelay:3];
});
运行一下发现并没有任何作用,是因为run方法只是尝试想要开启当前线程中的runloop,但是如果该线程中并没有任何事件(source、timer、observer)的话,并不会成功的开启。
所以代码要改一下:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self performSelector:@selector(test:) withObject:@"bbb" afterDelay:3];
[[NSRunLoop currentRunLoop] run];
});
打印一下,会发现3s后会正常输出:======>:bbb
需要注意的是:
对于该performSelector
延迟方法而言,如果在主线程中调用,那么test
方法也是在主线程中执行;如果是在子线程中调用,那么test
也会在该子线程中执行。
- 结论:
1.performSelector:withObject:
只是一个单纯的消息发送,就是对 objc_msgSend的封装,和时间没有一点关系,所以不需要添加到子线程的Runloop中也能执行。
2.performSelector:withObject:afterDelay:
是在内部创建了一个NSTimer,然后会添加到当前线程的Runloop中,如果添加在子线程,由于子线程中的runloop默认是没有启动的状态,所以我们需要把子线程的Runloop跑起来才可。而且还要注意调用的顺序,必须先把timer (performSelector:withObject:afterDelay:
)注册到runloop中,才可。
网友评论