一、
NSObject类提供了很多方法供我们使用,这些方法是添加到runloop的,所以如果没有开启runloop的话,不会运行(不过有个坑,请看下面介绍)。
/// 主线程
performSelectorOnMainThread:withObject:waitUntilDone:
performSelectorOnMainThread:withObject:waitUntilDone:modes:
/// 指定线程
performSelector:onThread:withObject:waitUntilDone:
performSelector:onThread:withObject:waitUntilDone:modes:
/// 针对当前线程
performSelector:withObject:afterDelay:
performSelector:withObject:afterDelay:inModes:
/// 取消,在当前线程,和上面两个方法对应
cancelPreviousPerformRequestsWithTarget:
cancelPreviousPerformRequestsWithTarget:selector:object:
下面提供的方法是在指定的线程运行aSelector
,一般情况下aSelector
会添加到指定线程的runloop。但,如果调用线程和指定线程为同一线程,且wait
参数设为YES,那么aSelector
会直接在指定线程运行,不再添加到runloop。
performSelectorOnMainThread:withObject:waitUntilDone:
performSelectorOnMainThread:withObject:waitUntilDone:modes:
performSelector:onThread:withObject:waitUntilDone:
performSelector:onThread:withObject:waitUntilDone:modes:
其实这也很好理解,假设这种情况也添加到指定线程的runloop,我们可以这样反向理解:
1、当前线程runloop还没有开启,那么aSelector
就不会被执行,然而你却一直在等待,造成线程卡死。
2、当前线程runloop已经开启,那么调用performSelector
这个方法的位置肯定是处于runloop的callout方法里面,在这里等待runloop再callout从而调用aSelector
方法完成,显然也是死等待,线程卡死。。。
还有一些performSelector
方法,是不会添加到runloop的,而是直接执行,可以按照上面的特殊情况进行理解。方法列举如下:
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
看到这里,是否感觉有些乱???只要记住没有延迟或者等待的都不会添加到runloop,有延迟或者等待的还有排除上面提到的特殊情况。
二、
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[self performSelector:@selector(test) withObject:nil afterDelay:2];
});
会发现test方法并没有被调用,因为子线程中的runloop默认是没有启动的状态。使用run方法开启当前线程的runloop,但是一定要注意run方法和执行该延迟方法的顺序。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[[NSRunLoop currentRunLoop] run];
[self performSelector:@selector(test) withObject:nil afterDelay:2];
});
会发现即便添加了run方法,但是test方法还是没有被调用,在最后打印当前线程的runloop,会发现:
timers = <CFArray 0x6000002a8100 [0x109f67bb0]>{type = mutable-small, count = 1, values = (
0 : <CFRunLoopTimer 0x6000001711c0 [0x109f67bb0]>{valid = Yes, firing = No, interval = 0, tolerance = 0, next fire date = 544280547 (1.98647892 @ 3795501066754), callout = (Delayed Perform) lZLearningFromInterviewController test (0x105ea0d9c / 0x104b2e2c0) (), context = <CFRunLoopTimer context 0x600000470080>}
子线程的runloop中确实添加了一个CFRunLoopTimer的事件,但是到最后都不会被执行。
将run方法和performSelector延迟方法调换顺序后运行:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[self performSelector:@selector(test) withObject:nil afterDelay:2];
[[NSRunLoop currentRunLoop] run];
});
此时test方法会被调用,分别打印执行完performSelecor和run方法之后,发现在执行完performSelector方法后该timer事件会被添加到子线程的runloop中:
timers = <CFArray 0x6000000b3c80 [0x112956bb0]>{type = mutable-small, count = 1, values = (
0 : <CFRunLoopTimer 0x60000016fc00 [0x112956bb0]>{valid = Yes, firing = No, interval = 0, tolerance = 0, next fire date = 544280800 (1.98171604 @ 4048676578329), callout = (Delayed Perform) lZLearningFromInterviewController test (0x10e88fd9c / 0x1
但是当执行完run方法之后,runloop中的timer事件已经是执行完的状态:
timers = <CFArray 0x6000000b3c80 [0x112956bb0]>{type = mutable-small, count = 0, values = ()},
所以在子线程中两者的顺序必须是先执行performSelector延迟方法之后再执行run方法。因为run方法只是尝试想要开启当前线程中的runloop,但是如果该线程中并没有任何事件(source、timer、observer)的话,并不会成功的开启。