博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS开发日记24-详解RunLoop
阅读量:5156 次
发布时间:2019-06-13

本文共 5091 字,大约阅读时间需要 16 分钟。

今天博主有一个RunLoop的需求,遇到了一些困难点,在此和大家分享,希望能够共同进步.

RunLoop是什么?

从字面上理解,Runloop指的就是运行循环,iOS中,只要程序启动, 就会创建Runloop,用来处理各种事件(比如定时器事件, 触摸事件等).

它的主要作用有以下几个:

1.保持程序的运行:如果没有它, 程序一启动就over了;

2.事件的处理: 包括按钮点击事件, 屏幕的点击事件等;

3.定时器处理: NSTimer的处理;

4.节约CPU, 提高性能:有任务的时候干活,没有任务时候休息.

在iOS中, 可以通过两套API来访问Runloop:

1.Foundation框架: OC中的框架, 直接访问NSRunLoop对象.

2.Core Foundation框架: c语言的框架, 通过CFRunLoopRef访问.

实际上, NSRunLoop和CFRunLoopRef是同一个对象, 只不过通过不同的API来访问的,NSRunLoop是对CFRunLoopRef的封装.

 

OC中, 使用下面代码就可以获得Runloop对象:

// Foundation

[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象

[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象

// Core Foundation

CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象

CFRunLoopGetMain(); // 获得主线程的RunLoop对象

 

Nslog打印Runloop对象, 里面的内容如下:(由于内容比较多, 只将重点内容列出)

// runloop对象 
{wakeup port = 0x1003, stopped = false, ignoreWakeUps = true, // 当前模式 current mode = UIInitializationRunLoopMode, common modes =
{contents = "UITrackingRunLoopMode"} 2 :
{contents = "kCFRunLoopDefaultMode"} } // source: 源 6 :
// observers: 观察者 observers =
// timer: 定时器 timers = (null), // 两种不同的模式 3 :
{name = GSEventReceiveRunLoopMode, port set = 0x2003, timer port = 0x2103, sources0 =
{type = mutable set, count = 1, entries => 0 :
{signalled = No, valid = Yes, order = -1, context =
{version = 0, info = 0x0, callout = PurpleEventSignalCallback (0x10d8236c8)}} }

要想知道上面内容是什么意思, 先看下面的内容:

Core Foundation中关于RunLoop的5个类1.CFRunLoopRef2.CFRunLoopModeRef 3.CFRunLoopSourceRef 4.CFRunLoopTimerRef 5.CFRunLoopObserverRef 可以这理解为, 一个Runloop对象里面, 必不可少的是CFRunLoopModeRef, Mode里面可以包含timer/source/oberver(只有包含了其中一个, Runloop才能在启动后保证不死)

层级结构如下图:

接下来, 对里面的内容说明一下:

一.CFRunLoopModeRef:代表RunLoop的运行模式    1.一个RunLoop包含若干个Mode,每个Mode又包含若干个Source/Timer/Observer    2.每次RunLoop启动时,只能指定其中一个 Mode,这个Mode被称作CurrentMode    3.这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响系统默认注册了5个Mode: 1.kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行 2.UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响 3.UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用 4.GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到 5.kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode, 被标记为kCFRunLoopCommonModes格式的都可以成为commonMode, 其中包含1.2两种模式. 二.CFRunLoopSourceRef:是事件源(输入源) 现在主要分为两种: Source0:非基于Port的 Source1:基于Port的(如GCD底层实现也是通过Port的, 主要用于线程之间的通信) 三.CFRunLoopTimerRef:是基于时间的触发器, 基本上说的就是NSTimer 四.CFRunLoopObserverRef:观察者, 可以用来监听Runloop的状态, 下面列出了Runloop的状态 typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) { kCFRunLoopEntry = (1UL << 0), // 即将进入 kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理timer kCFRunLoopBeforeSources = (1UL << 2), // 即将处理事件源 kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠 kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒 kCFRunLoopExit = (1UL << 7), // 即将退出Runloop };

下面图片描述了Runloop的运行和状态:

Runloop的寄生于线程:一个线程只能有唯一对应的runloop;但这个根runloop里可以嵌套子runloops;

自动释放池寄生于Runloop:程序启动后,主线程注册了两个Observer监听runloop的进出与睡觉。一个最高优先级OB监测Entry状态;一个最低优先级OB监听BeforeWaiting状态和Exit状态。
线程(创建)-->runloop将进入-->最高优先级OB创建释放池-->runloop将睡-->最低优先级OB销毁旧池创建新池-->runloop将退出-->最低优先级OB销毁新池-->线程(销毁)

Mach是XNU的内核,进程、线程和虚拟内存等对象通过端口发消息进行通信,Runloop通过mach_msg()函数发送消息,如果没有port 消息,内核会将线程置于等待状态 mach_msg_trap() 。如果有消息,判断消息类型处理事件,并通过modeItem的callback回调。

Runloop有两个关键判断点,一个是通过msg决定Runloop是否等待,一个是通过判断退出条件来决定Runloop是否循环。

 

应用1:滑动与图片刷新:

当tableview的cell上有需要从网络获取的图片的时候,滚动tableView,异步线程会去加载图片,加载完成后主线程就会设置cell的图片,但是会造成卡顿。可以让设置图片的任务在CFRunLoopDefaultMode下进行,当滚动tableView的时候,RunLoop是在 UITrackingRunLoopMode 下进行,不去设置图片,而是当停止的时候,再去设置图片。

- (void)viewDidLoad {

 

[super viewDidLoad];  // 只在NSDefaultRunLoopMode下执行(刷新图片) 

[self.myImageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@""] afterDelay:ti inModes:@[NSDefaultRunLoopMode]];  

 }

应用2:常驻子线程,保持子线程一直处理事件

为了保证线程长期运转,可以在子线程中加入RunLoop,并且给Runloop设置item,防止Runloop自动退出。

+ (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; } - (void)start { [self.lock lock]; if ([self isCancelled]) { [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; } else if ([self isReady]) { self.state = AFOperationExecutingState; [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; } [self.lock unlock]; } http://www.cnblogs.com/66it/p/4719701.html?utm_source=tuicool http://blog.ibireme.com/2015/05/18/runloop/

转载于:https://www.cnblogs.com/Twisted-Fate/p/4831510.html

你可能感兴趣的文章
iOS 组件化
查看>>
(转)Tomcat 8 安装和配置、优化
查看>>
(转)Linxu磁盘体系知识介绍及磁盘介绍
查看>>
tkinter布局
查看>>
命令ord
查看>>
Sharepoint 2013搜索服务配置总结(实战)
查看>>
博客盈利请先考虑这七点
查看>>
使用 XMLBeans 进行编程
查看>>
写接口请求类型为get或post的时,参数定义的几种方式,如何用注解(原创)--雷锋...
查看>>
【OpenJ_Bailian - 2287】Tian Ji -- The Horse Racing (贪心)
查看>>
Java网络编程--socket服务器端与客户端讲解
查看>>
List_统计输入数值的各种值
查看>>
学习笔记-KMP算法
查看>>
Timer-triggered memory-to-memory DMA transfer demonstrator
查看>>
跨域问题整理
查看>>
[Linux]文件浏览
查看>>
64位主机64位oracle下装32位客户端ODAC(NFPACS版)
查看>>
获取国内随机IP的函数
查看>>
今天第一次写博客
查看>>
江城子·己亥年戊辰月丁丑日话凄凉
查看>>