废话
我负责维护的已经生长了10多年代码中,富含各种pthread
与CFRunLoop
接口,pthread
很简单,一看就懂,一点就会。但对于RunLoop
的概念,从入门Mac开发到现在,看了很多资料也一直都不得其要领。(一定是我太笨!)
今日突然灵光一现,对此概念小有成就,是以发布此文,帮助新人理解。
NSRunLoop介绍
NSRunLoop
本质一个while ture的循环,但不仅仅是一个while ture循环,我们可以究极简化NSLoop为:
while(true){
sleep(1);
}
看起来,好像入门级小白也能写出来上方的代码,那么苹果为什么费这么大力气搞一个这样的玩意儿呢?
NSRunLoop
存在的原因之一,NSRunLoop在休眠时,不会占用系统任何资源!
NSRunLoop简单使用
在我入门时,遇到了一个奇怪的场景,具体的场景我忘了,但类似场景可由以下操作复现。
阶段一:无循环,无输出
创建一个控制台程序,代码如下:
#import
int main(){
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"窝窝头,1块钱4个,嘿嘿!");
});
return 0;
}
编译,运行,满怀期待地等待控制台输出,然而....什么也没输出,程序就关闭了。
这是因为:异步执行的代码还没开始执行的时候,主线程已经执行完操作退出了,所以异步执行的操作就没法完成了。
阶段二:死循环,无输出
那么问题来了,如果我想让异步执行的代码执行完之后,主线程再退出,应该怎么搞?相信聪明的读者肯定脑海中有想法了,那就是:
#import
int main(){
__block bool isFinish = false;
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"窝窝头,1块钱4个,嘿嘿!");
isFinish = true;
});
while(!isFinish){
sleep(1);
}
return 0;
}
编译,运行,等待....(3 thousand hours later)
看起来程序死循环了,为啥呢?
因为异步执行的代码我们选择的是主队列,而主队列此时正在“手动“写的RunLoop中阻塞着,无法抽身。
阶段三:死循环,有输出
我们"手动"写的RunLoop,会一直阻塞住,没有办法调度它去执行我们异步操作的代码,那么如果我们使用系统的NSRunLoop
呢?代码如下:
#import
int main(){
NSRunLoop *rl = [NSRunLoop currentRunLoop];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"窝窝头,1块钱4个,嘿嘿!");
});
[rl run];
return 0;
}
编译,运行,等待...终于出现了熟悉的窝窝头叫卖声,BUT,程序也一直RunLoop下去了。
但,通过上述例子,我相信各位能够体验到RunLoop是一个while ture循环这句话了。
阶段四:无循环,有输出
那么,问题来了,我如果不想让程序RunLoop怎么办呢?我只想让它输出叫卖声,然后程序结束。
我们可以进一步学习使用NSRunLoop
,等到异步操作完成之后,退出Runloop即可。
代码如下:
#import
int main(){
NSRunLoop *rl = [NSRunLoop currentRunLoop];
NSPort *p = [NSMachPort port];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"窝窝头,1块钱4个,嘿嘿!");
[rl removePort:p forMode:NSDefaultRunLoopMode];
});
[rl addPort:p forMode:NSDefaultRunLoopMode];
[rl runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
return 0;
}