转自【知识小集:霖溦】
先看一段代码
-(void)viewDidLoad{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1");
[self performSelector:@selector(test) withObject:nil afterDelay:0];
NSLog(@"2");
});
}
- (void)test{
NSLog(@"3");
}
这段代码的执行结果是1、2
原因
因为
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay
实际在runloop里面,是一个定时器,但是因为子线程,runloop是默认没有开启的
下面是苹果API的注释,就能解释这个问题:
image.png
想要执行-(void)test
方法,官方文档也提供了解决办法:
[self performSelectorOnMainThread:@selector(test) withObject:nil waitUntilDone:YES];
其实针对上述的逻辑,更简单的是:
[self performSelector:@selector(test) withObject:nil];
如果没有参数,可以再简单些:
[self performSelector:@selector(test)];
思考
-
其实我们常用的perform是
NSObject.h
这个头文件的方法;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
-
可以delay的,是
NSRunLoop.h
下的方法;
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
-
而之前提到的回调主线程的,是
NSThread.h
里的方法:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
虽然他们都是NSObject的方法或者是分类补充方法,但实际上,是隶属于不同模块的;
对比上面三个方法,后面两个是没有返回值的。这其实也是有官方注释的依据的:
image.png image.png
我们很多人应该总是会被上述的警告所困扰,大多数人的解决方式,就是利用类似下面的方式去屏蔽警告,这种做法虽然简单,但实际是有风险的:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
//code
#pragma clang diagnostic pop
其实除了利用 IMP 或者 NSInvocation 那种比较“高端”的方式,更多的情况下,在方法没有返回值时,或者我们不需要返回值时,我们可以用:
[self performSelector:@selector(test) withObject:nil afterDelay:0];
所以你比如在YYText
方法里也能看到[self performSelector:@selector(test) withObject:nil afterDelay:0];
这种调用方式,不能简单的认为是犯了低级错误,当我们不需要返回值的时候,按照官方文档的建议,我们可以用performSelectorOnMainThread
,也可以用[self performSelector:@selector(test) withObject:nil afterDelay:0];
,当然官方的是最严谨的,没有使用delay
的原因,就是因为多线程runloop的问题了
网友评论