今天在研究一个框架的时候,发现对方使用了cancelPerformSelector:target:argument:这个方法,遂打开文档研究了一下。
performSelector:target:argument:order:modes:和cancelPerformSelector:target:argument:是
NSRunLoop的一个叫NSOrderedPerform的Category中定义的两个方法。顾名思义,order有顺序的意思,即按某种顺序执行方法,没错,这个顺序方法就是performSelector:target:argument:order:modes:,这个方法的作用是按照参数order来先后执行target对象的方法,argument和modes分别是参数和运行循环的模式,这个分类还相应的有一个配套的取消方法,就是cancelPerformSelector:target:argument:,作用就是取消要执行的selector参数,注意,上面的selector都是target的方法,argument都是target的参数,如果误代可能会引起奔溃。
显然,既然它们都是NSRunLoop的方法,那么,它们必须要在某条开启运行循环的线程中使用才有效。下面我们来测试一下:新建一个project,接着运行下面的代码:
[[NSRunLoop currentRunLoop]performSelector:@selector(doTip) target:self argument:nil order:0 modes:@[(NSRunLoopMode)kCFRunLoopDefaultMode]];
- (void)doTip {
NSLog(@"%s", __func__);
}
运行之后控制器打印

接着,我们把代码改成
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [[NSRunLoop currentRunLoop]performSelector:@selector(doTip) target:self argument:nil order:0 modes:@[(NSRunLoopMode)kCFRunLoopDefaultMode]]; });
运行,控制台毫无反应。
为什么之前的代码可以而这段代码不行?
这是因为,之前的代码是默认在主线乘中执行的,而主线程在被创建之后就已经开启运行循环,而在这段代码中我们使用全局队列,但是并没有开启队列中线程的运行循环,所以我们需要把代码补齐,把这段代码改为
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSPort port] forMode:(NSRunLoopMode)kCFRunLoopCommonModes]; [runLoop performSelector:@selector(doTip) target:self argument:nil order:10 modes:@[(NSRunLoopMode)kCFRunLoopCommonModes]];
[runLoop run];
再次运行,控制台又有输出了。});
之前说过,performSelector:target:argument:order:modes:和顺序有关,在这里参数order是一个NSUInteger类型,那是什么顺序,这里我们也测试一下,更换代码:
[[NSRunLoop currentRunLoop]performSelector:@selector(doTip5) target:self argument:nil order:5 modes:@[(NSRunLoopMode)kCFRunLoopDefaultMode]]; [[NSRunLoop currentRunLoop]performSelector:@selector(doTip2) target:self argument:nil order:2 modes:@[(NSRunLoopMode)kCFRunLoopDefaultMode]]; [[NSRunLoop currentRunLoop]performSelector:@selector(doTip3) target:self argument:nil order:3 modes:@[(NSRunLoopMode)kCFRunLoopDefaultMode]]; [[NSRunLoop currentRunLoop]performSelector:@selector(doTip1) target:self argument:nil order:1 modes:@[(NSRunLoopMode)kCFRunLoopDefaultMode]]; [[NSRunLoop currentRunLoop]performSelector:@selector(doTip4) target:self argument:nil order:4 modes:@[(NSRunLoopMode)kCFRunLoopDefaultMode]];
运行:- (void)doTip1 { NSLog(@"%s", __func__); } - (void)doTip2 { NSLog(@"%s", __func__); } - (void)doTip3 { NSLog(@"%s", __func__); } - (void)doTip4 { NSLog(@"%s", __func__); } - (void)doTip5 { NSLog(@"%s", __func__); }
我们仔细观察代码的书写顺序和运行结果就会发现,参数order越小,执行越早,也就是说,最早执行的代码是order最小的那个,然后按顺序进行下去。个人觉得这个方法真是强大。
接下去我们讲它的配对,取消执行方法:cancelPerformSelector:target:argument:
直接上代码:
运行:[[NSRunLoop currentRunLoop]performSelector:@selector(doTip) target:self argument:nil order:1 modes:@[(NSRunLoopMode)kCFRunLoopDefaultMode]]; [[NSRunLoop currentRunLoop] cancelPerformSelector:@selector(doTip) target:self argument:nil];
控制台啥都没有!
没错,cancelPerformSelector:target:argument:直接把performSelector:target:argument:order:modes:要执行的selector给取消了,就这样没了。