内容简介:自己平常开发中比较少用到先看一段代码:执行结果如下:没有打印出2,只打印出了1和3。
自己平常开发中比较少用到 performSelector 相关的API,但是平常看些第三方的时候,发现第三方作者用到 performSelector 相关的API比较多。自己理解的是,可以在一定程度上解耦,不必引入相关类。但是最近在用到时,遇到了一些问题。由此,查看了一些博客,自己也做了验证,在此记录一下。
先看一段代码:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"1");
[self performSelector:@selector(testPerform) withObject:nil afterDelay:0];//
NSLog(@"3");
});
}
- (void)testPerform{
NSLog(@"2");
}
复制代码
执行结果如下:没有打印出2,只打印出了1和3。
testPerform
。
官方注释:
那按照官方文档说明在子线程中加入runloop,看下执行效果。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"1");
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[self performSelector:@selector(testPerform) withObject:nil afterDelay:0];//
NSLog(@"3");
});
}
复制代码
通过获取当前的runloop,系统就会返回当前的runloop,如果没有的话,会创建后返回,但是加入了runloop的时候,执行结果,依然是只有打印出来1和3,没有打印2。在 [self performSelector:@selector(testPerform) withObject:nil afterDelay:0] 方法调用前后,通过控制台打印runloop对象,确实看到了调用方法后,runloop里多了一个timer源。
前后runloop对比:
testPerform 执行的timer,为什么还依然没有执行。因为runloop没有跑起来。 所以创建完runloop后,还需要runloop跑起来。【通过给当前runloop添加观察者,查看runloop的状态,runloop没有跑起来】当我们调用
[runloop run]; 方法后,将runloop跑起来后,
testPerform
才会执行。打印结果为1,2,3。
但,问题又来了,既然加入了runloop,并且跑起来了,为什么3还会打印出来,runloop不是相当于死循环吗?循环外的3为什么会打印出来?这个问题,通过加入的runloop的观察者的打印情况可以看出来,是因为,runloop在执行完 testPerform 后,就退出了。所以下边的3页打印出来了。
观察者打印:
testPerform 方法内打印runloop,看到此时runloop对象的timers数组里边已经是空的了。runloop的mode里没有source1、没有source0、也没有timer源,所以就退出了】由此,也可以猜测:在runloop里设置的timer触发
[self performSelector:@selector(testPerform) withObject:nil afterDelay:0]
方法后,该timer就销毁了。
怎样让runloop不退出呢?给当前runloop加入事件源或定时器temers,当前runloop就不会退出了,只是在不需要执行任务的时候进入休眠。
repeats 参数要设置为YES,否则执行完timer之后,runloop就不再持有timer,runloop就退出来了。还可以通过
[runloop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
加入事件源的方法,使runloop一直不退出。】
还有一个方法是 - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes; 这个方法多了一个设置mode的参数,可以通过这个参数设置在timer在哪个mode下执行,读者可自己检测。
添加runloop观察者的代码:
- (void)addObserver
{
/*
kCFRunLoopEntry = (1UL << 0),1
kCFRunLoopBeforeTimers = (1UL << 1),2
kCFRunLoopBeforeSources = (1UL << 2), 4
kCFRunLoopBeforeWaiting = (1UL << 5), 32
kCFRunLoopAfterWaiting = (1UL << 6), 64
kCFRunLoopExit = (1UL << 7),128
kCFRunLoopAllActivities = 0x0FFFFFFFU
*/
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case 1:
{
NSLog(@"进入runloop");
}
break;
case 2:
{
NSLog(@"timers");
}
break;
case 4:
{
NSLog(@"sources");
}
break;
case 32:
{
NSLog(@"即将进入休眠");
}
break;
case 64:
{
NSLog(@"唤醒");
}
break;
case 128:
{
NSLog(@"退出");
}
break;
default:
break;
}
});
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);//将观察者添加到common模式下,这样当default模式和UITrackingRunLoopMode两种模式下都有回调。
self.obsever = observer;
CFRelease(observer);
}
复制代码
本篇记录算是自己的理解,水平有限,如果有错误的地方,请批评指正,会尽快修改。
参考致谢:
以上所述就是小编给大家介绍的《Runloop与performSelector》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Just My Type
Simon Garfield / Profile Books / 2010-10-21 / GBP 14.99
What's your type? Suddenly everyone's obsessed with fonts. Whether you're enraged by Ikea's Verdanagate, want to know what the Beach Boys have in common with easy Jet or why it's okay to like Comic Sa......一起来看看 《Just My Type》 这本书的介绍吧!