无忌哥哥跟Runloop的孽缘

前言:

一次知道Runloop,是在处理定时器的时候碰到的,当时觉得很难(实际上也挺难的),从字面的意思上看又跑圈 / 运行循环的意思, 其实也差不多;

Runloop的作用:

保持程序的持续运行; 处理APP中的各种事件;节省CPU的资源;

为什么会出现Runloop?

如果没有Runloop,程序只能从上到下执行, 只会运行一遍,从这歌问题考虑,可以稍微的理解Runloop的原理实际上就是一个while大循环;

默认启动的Runloop都是跟主线程相关联的;

什么是Runloop?

1.从字面意思看,就是一个运行循环;

2.在它内部 是一个do- while循环,不断的处理各种任务 ;

3.一个线程对应一个runloop,主线程的runllop默认已经启动,子线程的runllop需要自己手动启动;

4.runloop只能选择一个模式启动,如果当前model中没有任何 source timer observe,那么就直接退出runloop;

实现Runloop的方式?

iOS中有有两套api可以获取 访问 和使用Runloop;

Foundation提供的NSRunloop;

Core Foundation 提供的CFRunLoopRef;

NSRunloop  跟 CFRunLoopRef 都代表 runloop对象;

NSRunloop是基于CFRunloop的,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API;

Runloop跟线程的关系?

每条线程都有唯一一个与之对应的Runloop对象;

主线程的Runloop默认创建,子线程手动创建;

Runloop在第一时间创建, 在线程消亡的时候 死亡, Runloop是寄生在线程上的;

Runloop的相关类 

CFRunLoopRef 

CFRunLoopModeRef

CFRunLoopSourceRef

CFRunLoopTimeRef

CFRunLoopObserverRef

runloop饰事件驱动,如果runllop中没有 source time observer ,runloop 就会一直睡下去;


什么时候使用runloop?

1.开启一个常驻线程(让一个子线程不进去消亡状态,等待其他线程发来消息,处理其他事件);

2.在子线程中开一个定时器;

3.在子线程中进行一些长期监控;

4.可以控制定时器在那那种模式下执行;

5.可以让某些事件(行为/任务)在特定的模式下执行;

6.可以添加observer监听runloop事件

事件循环:

每个线程都有一个runloop对象,只有主线程的runloop默认时开启的,在子线程中我们调用[NSRunLoop currentRunLoop]; 获取RunLoop对象的时候,就会创建RunLoop; 一个线程可以开辟多个runloop,都是嵌套在最大的runloop中;

runloop的一些优点:

保证程序持续运行;

处理app中的各种事件

节省cpu时间,在程序启动后,如果什么都没有执行的话,就进入睡眠状态;

runloop的构成元素:


1.CFRunLoopModeRef: 代表Runloop的运行模式;

一个Runloop包含n多个model,每个model中又包含了n多个source/timer/obsercver;

一个Runloop在同一时间内只能处于一种模式下工作,这个模式也就是currentMode;

一个Runloop的mode 是可以自由切换的;

系统默认注册了5种运行mode:

NSDefaultRunLoopMode: 默认的Mode也是空闲线程,通常主线程的RunLoop是在这个Mode下运行

UITrackingRunLoopMode: 界面跟踪Mode,当用户与界面交互的时候会在此Mode下运行

NSRunLoopCommonModes: 这个不是一种真正的Mode,是一个占位用的Mode

UIInitializationRunLoopMode: 程序启动时(初始化)的Mode,启动完成后就不在此Mode下

GSEventReceiveRunLoopMode: 接受系统事件的内部Mode,一般我们用不到

NSRunLoopCommonModes的应用实例:

当我们在做图片轮播器的时候,如果使用的是kCFRunLoopDefaultMode那么当ScrollView滚动的时候,RunLoop模式就会切换为UITrackingRunLoopMode,这时候NSTimer就没法执行,这时候我们可以使用kCFRunLoopCommonModes,就可以解决这个问题。


2. CFRunLoopTimerRef

CFRunLoopTimerRef是基于时间的触发器(时间到了就触发)

CFRunLoopTimerRef基本上说的就是NSTimer,它受RunLoop Mode的影响;

另外 CADisplayLink也可以在Runloop上跑

3. CFRunLoopSourceRef  (事件源/输入源)

按照官方文档CFRunLoopSourceRef为3类:

1. Port-Based Sources:与内核相关,基于端口,跟其它线程进行交互的;

2. Custom Input Sources:与自定义Sources相关;

3. Cocoa Perform Selector Sources:与Performxxxxxx等方法等相关

按照函数调用栈CFRunLoopSourceRef分2类:

1. Source0:非基于Port的;

2. Source1:基于Port的,通过内核和其他线程通信,接收、分发系统事件;

4. CFRunLoopObserverRef

CFRunLoopObserverRef是RunLoop的观察者,可以通过CFRunLoopObserverRef来监听RunLoop状态的改变,CFRunLoopObserverRef监听的状态由以下几种:

kCFRunLoopEntry = (1UL << 0), 

kCFRunLoopBeforeTimers = (1UL << 1),

kCFRunLoopBeforeSources = (1UL << 2),

kCFRunLoopBeforeWaiting = (1UL << 5),

kCFRunLoopAfterWaiting = (1UL << 6),

kCFRunLoopExit = (1UL << 7),

kCFRunLoopAllActivities = 0x0FFFFFFFU

监听runloop的状态:

1.创建CFRunLoopObserverRef

// 第一个参数用于分配该observer对象的内存

// 第二个参数用以设置该observer所要关注的的事件,详见回调函数myRunLoopObserver中注释

// 第三个参数用于标识该observer是在第一次进入run loop时执行还是每次进入run loop处理时均执行

// 第四个参数用于设置该observer的优先级,一般为0

// 第五个参数用于设置该observer的回调函数

// 第六个参数observer的运行状态

CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {

NSLog(@"----监听到RunLoop状态发生改变---%zd", activity);

});

2.将观察者CFRunLoopObserverRef添加到RunLoop上面

CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);

3.观察者CFRunLoopObserverRef要手动释放 (并不遵循ARC,需要手动释放,不然会有内存问题)

CFRelease(observer);

runloop的处理逻辑


上图显示了线程的输入源

1.基于端口的输入源(Port Sources)

2.自定义输入源(Custom Sources)

3.Cocoa执行Selector的源(performSelectorxxxx方法)

4.定时源(Timer Sources )

线程针对上面不同的输入源,有不同的处理机制

1.handlePort——处理基于端口的输入源

2.customSrc——处理用户自定义输入源

3.mySelector——处理Selector的源

4.timerFired——处理定时源

runloop的具体使用:

1. 可以让图片延时显示:(重绘试图)

比如要 渲染很多图片,但是又不希望出现卡顿的情况, 可以考虑使用 NSDefaultRunLoopMode 来实现 ;

当相关的视图对象接收到设置属性的消息的时候,就会将自己标记为要重绘。RunLoop会收集所有等待重绘制的视图,苹果会注册一个CFRunLoopObserver来监听kCFRunLoopBeforeWaiting事件,当事件触发的时候,就会对所有等待重绘的视图对象发送drawRect:消息。

[self.headerIV performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"1"] afterDelay:3.0f inModes:@[NSDefaultRunLoopMode]];

2.常驻线程 (保证一个线程永远不死)

self.thread = [[ZKThread alloc]initWithTarget:self selector:@selector(execute) object:nil];

[self.thread start];

[self performSelector:@selector(runAction) onThread:self.thread withObject:nil waitUntilDone:NO];

- (void)run{

NSLog(@"run----%@",[NSThread currentThread]);

[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];

[[NSRunLoop currentRunLoop] run];

NSLog(@"----------");

}

3.一个TableView延迟加载图片的新思路

当cell上有需要从网络获取的图片的时候,我们滚动tableView,异步线程会去加载图片,加载完成后主线程就会设置cell的图片,这个时候就会出现卡的现象。

一般的解决方案是调用tableView的代理方法,判断tableView是否正在滑动,如果在滑动,就不设置图片,等停止滑动后再去设置cell的图片。用Runloop能更简单的解决这个问题。我们可以根据RunLoop不同Mode下,执行不同的事件来解决这个问题思路如下:

当设置图片的时候,让其在 CFRunLoopDefaultMode 下进行。当滚动tableView的时候,RunLoop是在

UITrackingRunLoopMode 这个Mode下,就不会设置图片,当停止的时候,就会设置图片。

UIImage*downloadedImage = ...;[self.avatarImageViewperformSelector:@selector(setImage:)withObject:downloadedImageafterDelay:0inModes:@[NSDefaultRunLoopMode]];

4.让Crash的App回光返照

App崩溃的发生分两种情况:

(1)program received signal:SIGABRT SIGABRT一般是过度release 或者 发送 unrecogized selector导致。

由 SIGABRT 引起的Crash 是系统发这个signal给App,程序收到这个signal后,就会把主线程的RunLoop杀死,程序就Crash了 该例只针对 SIGABRT引起的Crash有效。

Signal:

是Unix、类Unix等操作系统中进程间通讯的一种方式,用来通知一个事件发生。当一个singal发送给进程,操作系统就会中断进程的正常控制流程,

如果在进程中定义了信号的处理函数,那么这个函数就会被执行,因此我们可以注册signal,并指定收到signal后要执行的函数

为了让App回光返照,我们需要来捕获libsystem_sim_c.dylib调用 abort() 函数发出的程序终止信号,然后让其执行我们定义的处理signal的方法。在方法中,我们需要开启一个RunLoop,保持主线程不退出。

(2)EXC_BAD_ACCESS是访问已被释放的内存导致,野指针错误。

参考文档:http://www.jianshu.com/p/4bc01f5269e7 http://www.jianshu.com/p/549c37f60bf7

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容