【runloop】performSelector方法探究

一、

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)的话,并不会成功的开启。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值