iOS-RunLoop
简单的说run loop是事件驱动的一个大循环,如下代码所示
int main(int argc, char * argv[]) {
//程序一直运行状态
while (AppIsRunning) {
//睡眠状态,等待唤醒事件
id whoWakesMe = SleepForWakingUp();
//得到唤醒事件
id event = GetEvent(whoWakesMe);
//开始处理事件
HandleEvent(event);
}
return 0;
}
Cocoa会涉及到Run Loops的
系统级:GCD,mach kernel,block,pthread
应用层:NSTimer,UIEvent,Autorelease,NSObject(NSDelayedPerforming),
NSObject(NSThreadPerformAddition),CADisplayLink,CATransition,CAAnimation,dispatch_get_main_queue()(GCD中dispatch到main queue的block会被dispatch到main RunLoop执行),NSPort,NSURLConnection,AFNetworking(这个第三方网络请求框架使用在开启新线程中添加自己的run loop监听事件)
RunLoop原理(执行顺序的伪代码)
SetupThisRunLoopRunTimeoutTimer(); // by GCD timer
do {
__CFRunLoopDoObservers(kCFRunLoopBeforeTimers);
__CFRunLoopDoObservers(kCFRunLoopBeforeSources);
__CFRunLoopDoBlocks();
__CFRunLoopDoSource0();
CheckIfExistMessagesInMainDispatchQueue(); // GCD
__CFRunLoopDoObservers(kCFRunLoopBeforeWaiting);
var wakeUpPort = SleepAndWaitForWakingUpPorts();
// mach_msg_trap
// Zzz...
// Received mach_msg, wake up
__CFRunLoopDoObservers(kCFRunLoopAfterWaiting);
// Handle msgs
if (wakeUpPort == timerPort) {
__CFRunLoopDoTimers();
} else if (wakeUpPort == mainDispatchQueuePort) {
// GCD
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__()
} else {
__CFRunLoopDoSource1();
}
__CFRunLoopDoBlocks();
} while (!stop && !timeout);
构成
Thread包含一个CFRunLoop,一个CFRunLoop包含一种CFRunLoopMode,mode包含CFRunLoopSource,CFRunLoopTimer和CFRunLoopObserver。
CFRunLoopMode
RunLoop只能运行在一种mode下,如果要换mode当前的loop也需要停下重启成新的。利用这个机制,ScrollView过程中 NSDefaultRunLoopMode的mode会切换UITrackingRunLoopMode来保证ScrollView的流畅滑动不受只能在 NSDefaultRunLoopMode时处理的事件影响滑动。同时mode还是可定制的。
- NSDefaultRunLoopMode:默认,空闲状态
- UITrackingRunLoopMode:ScrollView滑动时
- UIInitializationRunLoopMode:启动时
- NSRunLoopCommonModes:Mode集合 Timer计时会被scrollView的滑动影响的问题可以通过将timer添加到NSRunLoopCommonModes来解决
//将timer添加到NSDefaultRunLoopMode中
[NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(timerTick:)
userInfo:nil
repeats:YES];
//然后再添加到NSRunLoopCommonModes里
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:@selector(timerTick:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
CFRunLoopTimer
NSTimer是对RunLoopTimer的封装
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
+ (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel;
- (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode;
CFRunLoopSource
- source0:处理如UIEvent,CFSocket这样的事件
- source1:Mach port驱动,CFMachport,CFMessagePort
CFRunLoopObserver
Cocoa框架中很多机制比如CAAnimation等都是由RunLoopObserver触发的。observer到当前状态的变化进行通知。
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0),
kCFRunLoopBeforeTimers = (1UL << 1),
kCFRunLoopBeforeSources = (1UL << 2),
kCFRunLoopBeforeWaiting = (1UL << 5),
kCFRunLoopAfterWaiting = (1UL << 6),
kCFRunLoopExit = (1UL << 7),
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
使用RunLoop的案例
AFNetworking
使用NSOperation+NSURLConnection并发模型都会面临NSURLConnection下载完成前线程退出导致NSOperation对象接收不到回调的问题。AFNetWorking解决这个问题的方法是按照官方的guidhttps://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html#//apple_ref/occ/instm/NSURLConnection/initWithRequest:delegate:startImmediately:上写的NSURLConnection的delegate方法需要在connection发起的线程runloop中调用,于是AFNetWorking直接借鉴了Apple自己的一个Demohttps://developer.apple.com/LIBRARY/IOS/samplecode/MVCNetworking/Introduction/Intro.html的实现方法单独起一个global thread,内置一个runloop,所有的connection都由这个runloop发起,回调也是它接收,不占用主线程,也不耗CPU资源。
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread =
[[NSThread alloc] initWithTarget:self
selector:@selector(networkRequestThreadEntryPoint:)
object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
类似的可以用这个方法创建一个常驻服务的线程。
TableView中实现平滑滚动延迟加载图片
利用CFRunLoopMode的特性,可以将图片的加载放到NSDefaultRunLoopMode的mode里,这样在滚动UITrackingRunLoopMode这个mode时不会被加载而影响到。
UIImage *downloadedImage = ...;
[self.avatarImageView performSelector:@selector(setImage:)
withObject:downloadedImage
afterDelay:0
inModes:@[NSDefaultRunLoopMode]];
接到程序崩溃时的信号进行自主处理例如弹出提示等
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
NSArray *allModes = CFBridgingRelease(CFRunLoopCopyAllModes(runLoop));
while (1) {
for (NSString *mode in allModes) {
CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);
}
}
异步测试
- (BOOL)runUntilBlock:(BOOL(^)())block timeout:(NSTimeInterval)timeout
{
__block Boolean fulfilled = NO;
void (^beforeWaiting) (CFRunLoopObserverRef observer, CFRunLoopActivity activity) =
^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
fulfilled = block();
if (fulfilled) {
CFRunLoopStop(CFRunLoopGetCurrent());
}
};
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, 0, beforeWaiting);
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
// Run!
CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, false);
CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
CFRelease(observer);
return fulfilled;
}
//NSRunLoop与NSTimer的联系
在Windows时代,大家肯定对SendMessage,PostMessage,GetMessage有所了解,这些都是windows中的消息处理函数,那对应在ios中是什么呢,其实就是NSRunloop这个东西。在ios中,所有消息都会被添加到NSRunloop中,分为‘input source’跟'timer source'种,并在循环中检查是不是有事件需要发生,如果需要那么就调用相应的函数处理。
我们在使用NSTimer的时候,可能会接触到runloop的概念,下面是一个简单的例子:

1 - (void)viewDidLoad
2 {
3 [super viewDidLoad];
4 // Do any additional setup after loading the view, typically from a nib.
5 NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1
6 target:self
7 selector:@selector(printMessage:)
8 userInfo:nil
9 repeats:YES];
10 }

这个时候如果我们在界面上滚动一个scrollview,那么我们会发现在停止滚动前,控制台不会有任何输出,就好像scrollView在滚动的时候将timer暂停了一样,在查看相应文档后发现,这其实就是runloop的mode在做怪。
runloop可以理解为cocoa下的一种消息循环机制,用来处理各种消息事件,我们在开发的时候并不需要手动去创建一个runloop,因为框架为我们创建了一个默认的runloop,通过[NSRunloop currentRunloop]我们可以得到一个当前线程下面对应的runloop对象,不过我们需要注意的是不同的runloop之间消息的通知方式。
接着上面的话题,在开启一个NSTimer实质上是在当前的runloop中注册了一个新的事件源,而当scrollView滚动的时候,当前的MainRunLoop是处于UITrackingRunLoopMode的模式下,在这个模式下,是不会处理NSDefaultRunLoopMode的消息(因为RunLoop Mode不一样),要想在scrollView滚动的同时也接受其它runloop的消息,我们需要改变两者之间的runloopmode.
1 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
简单的说就是NSTimer不会开启新的进程,只是在Runloop里注册了一下,Runloop每次loop时都会检测这个timer,看是否可以触发。当Runloop在A mode,而timer注册在B mode时就无法去检测这个timer,所以需要把NSTimer也注册到A mode,这样就可以被检测到。
说到这里,在http异步通信的模块中也有可能碰到这样的问题,就是在向服务器异步获取图片数据通知主线程刷新tableView中的图片时,在tableView滚动没有停止或用户手指停留在屏幕上的时候,图片一直不会出来,可能背后也是这个runloop的mode在做怪,嘿嘿。
iOS-RunLoop的更多相关文章
- iOS runloop 资源汇总-b
RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理.之后会介绍一下在 iOS 中,苹果是如何利 ...
- iOS Runloop理解
一.RunLoop的定义 当有持续的异步任务需求时,我们会创建一个独立的生命周期可控的线程.RunLoop就是控制线程生命周期并接收事件进行处理的机制. RunLoop是iOS事件响应与任务处理最核心 ...
- iOS RunLoop详解
1. RunLoop简介 1.1 什么是RUnLoop 可以理解为字面的意思:Run表示运行,Loop表示循环.结合在一起就是运行的循环.通常叫做运行循环. RunLoop实际上是一个对象,这个对象在 ...
- iOS runLoop 理解
目录 概述 run loop modes 一.概述 run loop叫事件处理循环,就是循环地接受各种各样的事件.run loop是oc用来管理线程里异步事件的工具.一个线程通过run loop可以监 ...
- iOS Runloop 消息循环
介绍 Runloop是一种事件监听循环,可以理解成一个while死循环,监听到事件就起来,没有就休息. Runloop可以在不同模式下进行切换,iOS有五种模式,其中UIInitializationR ...
- ios runloop学习
今天突然才之间才意识到NSTimer这样的运行方式,是在多线程中实现的循环还是在主线程中去实现的呢.当然不可能是在主线程中的while那么简单,那样什么都干不了,简单看了下NSTimer是以同步方式运 ...
- iOS runLoop 原理多线程 总结 NSTimer优化
可以理解为字面意思:Run 表示运行,Loop 表示循环.结合在一起就是运行的循环的意思.哈哈,我更愿意翻译为『跑圈』.直观理解就像是不停的跑圈. RunLoop 实际上是一个对象,这个对象在循环中用 ...
- iOS RunLoop了解和使用
RunLoop介绍和使用 上次讲了runtime,这次是runloop,虽然两者都是run开头的名词术语,但是在OC中,这两个东西压根没啥联系.这篇文章主要讲讲runloop的一些概念和用法.其中包含 ...
- iOS RunLoop简介
一.什么是RunLoop? RunLoop是运行循环,每个Cocoa应用程序都由一个处于阻塞状态的do/while循环驱动,当有事件发生时,就把事件分派给合适的监听器,如此反复直到循环停止.处理分派的 ...
- IOS RunLoop面试题
一 什么是RunLoop? 从字面意思看就是运行循环,其实内部就是do-while循环,这个循环内部不断地处理各种任务(比 如Source,Timer,Observer) 一个线程对应一个RunLoo ...
随机推荐
- python序列,字典备忘
初识python备忘: 序列:列表,字符串,元组len(d),d[id],del d[id],data in d函数:cmp(x,y),len(seq),list(seq)根据字符串创建列表,max( ...
- 【读书笔记】2016.12.10 《构建高性能Web站点》
本文地址 分享提纲: 1. 概述 2. 知识点 3. 待整理点 4. 参考文档 1. 概述 1.1)[该书信息] <构建高性能Web站点>: -- 百度百科 -- 本书目录: 第1章 绪论 ...
- 如何在ASP.NET Web站点中统一页面布局[Creating a Consistent Layout in ASP.NET Web Pages(Razor) Sites]
如何在ASP.NET Web站点中统一页面布局[Creating a Consistent Layout in ASP.NET Web Pages(Razor) Sites] 一.布局页面介绍[Abo ...
- ActionBar设置自定义setCustomView()留有空白的问题
先来看问题,当我使用ActionBar的时候,设置setCustomView时,会留有空白的处理 网上很多朋友说可以修改V7包到19,结果处理的效果也是不理想的. 下面贴出我觉得靠谱的处理代码 pub ...
- 静态库介绍与简单演练及同名资源冲突解决(.a格式的静态库)
1.静态库和动态库都是闭源库,不公开源代码. 静态库:.a和.framework 动态库:.dylib和.framework(iOS9取消了.dylib,使用.tbd替代) 2.静态库和动态库在使用上 ...
- MBProgressHUD 显示后,为何不能点击屏幕其他地方
解决办法: hud.userInteractionEnabled= NO; 就是这么简单粗暴!这个问题 我一定要发到首页候选区! /********************************* ...
- Objective-C之KVC、KVO
1,KVC(键值编码) Key Value Coding 1.1在C#中,可以通过字符串反射来获取对象,从而对对象的属性进行读写,Object-C中有同样的实现,通过字符串(属性名词)对对象的属性进 ...
- [Erlang 0126] 我们读过的Erlang论文
我在Erlang Resources 豆瓣小站上发起了一个征集活动 [链接] ,"[征集] 我们读过的Erlang论文",希望大家来参加.发起这样一个活动的目的是因为Erlang相 ...
- 怎么修改与设置.jsp文件属性?
1.首先 打开 >> Myeclipse或Eclipse.(我用的是Myeclipse) 2.打开 >> Window >> Preferences ...
- AngularJS 系列 01 - HelloWorld和数据绑定
目录导读: AngularJS 系列 学习笔记 目录篇 前言: 好记性不如烂键盘,随笔就是随手笔记,希望以后有用. 本篇目录: 1. Hello World 2. AngularJS中的数据绑定 3. ...