小码哥iOS学习笔记第十七天: Runloop基本认识

1、什么是Runloop

  • 顾名思义, Runloop就是运行循环, 在程序运行过程当中循环作一些事情bash

  • 应用范畴网络

    • 定时器(Timer)、PerformSelector
    • GCD Async Main Queue
    • 事件响应、手势识别、界面刷新
    • 网络请求
    • AutoreleasePool
  • 一个OC程序, main函数是这样的app

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
复制代码
  • 程序打开后, 会一直运行

  • 若是没有Runloop, main函数返回0
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return 0;
    }
}
复制代码
  • 此时程序打开后, 会直接关闭, 没法一直运行

  • 因此能够写出Runloop的伪代码, 以下图所示

  • do-while是一个死循环, 当没有任何消息发生的时候, 程序处于睡眠状态等待消息发生
  • 当消息产生后, 就会处理消息, 接着继续睡眠等待, 程序并不会立刻退出,而是保持运行状态
RunLoop的基本做用
保持程序的持续运行
处理App中的各类事件(好比触摸事件、定时器事件等)
节省CPU资源,提升程序性能:该作事时作事,该休息时休息
......
复制代码

2、Runloop对象

  • 在iOS中, 有两套API来访问和使用Runloop
    • Foundation: NSRunLoop
    • Core Foundation: CFRunLoopRef
  • NSRunLoop和CFRunLoopRef都表明着RunLoop对象

3、RunLoop与线程

  • 每条线程都有惟一的一个与之对应的RunLoop对象
  • RunLoop保存在一个全局的Dictionary里, 线程做为key, RunLoop对象作为Value
  • 线程刚建立时并无RunLoop对象, RunLoop会在第一次获取它时建立
  • RunLoop会在线程结束时销毁
  • 主线程的RunLoop已经自动获取(建立), 子线程默认没有开启RunLoop

4、获取RunLoop对象

  • 能够经过FoundationCore Foundation来获取RunLoop

一、Foundation

  • 获取当前线程的RunLoop
[NSRunLoop currentRunLoop];
复制代码
  • 获取主线程的RunLoop
[NSRunLoop mainRunLoop];
复制代码

二、Core Foundation

  • 获取当前线程的RunLoop
CFRunLoopGetCurrent()
复制代码
  • 获取主线程的RunLoop
CFRunLoopGetMain()
复制代码

5、RunLoop相关的类

  • RunLoop相关的类有五个
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
复制代码
  • CFRunLoopRef实际类型是__CFRunLoop

  • 主要成员结构有下面几个
typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoop * CFRunLoopRef;
struct __CFRunLoop {
    pthread_t _pthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
};
复制代码
  • _modes中存放的是CFRunLoopModeRef类型数据, 其中就有_currentMode, 只不过_currentMode是当前使用的mode函数

  • CFRunLoopModeRef的结构以下oop

  • 主要成员变量有下面几个
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
    CFStringRef _name;
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
};
复制代码
  • 其中的_sources0_sources1CFRunLoopSourceRef类型数据
  • 其中的_observersCFRunLoopObserverRef类型数据
  • 其中的_timersCFRunLoopTimerRef类型数据

6、CFRunLoopModeRef

  • CFRunLoopModeRef表明RunLoop的运行模式
  • 一个RunLoop包含若干个Mode,每一个Mode又包含若干个Source0/Source1/Timer/Observer
  • RunLoop启动时只能选择其中一个Mode,做为currentMode
  • 若是须要切换Mode,只能退出当前Loop,再从新选择一个Mode进入
    • 不一样组的Source0/Source1/Timer/Observer能分隔开来,互不影响
  • 若是Mode里没有任何Source0/Source1/Timer/ObserverRunLoop会立马退出

常见的两种CFRunLoopModeRef

  • kCFRunLoopDefaultModeNSDefaultRunLoopMode): App的默认Mode,一般主线程是在这个Mode下运行
  • UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其余 Mode 影响

7、RunLoop的运行逻辑

  • Source0
    • 触摸事件处理
    • performSelector:onThread:
  • Source1
    • 基于Port的线程间通讯
    • 系统事件捕捉
  • Timers
    • NSTimer
    • performSelector:withObject:afterDelay:
  • Observers
    • 用于监听RunLoop的状态
    • UI刷新(BeforeWaiting)
    • Autorelease pool(BeforeWaiting)
  • 能够打印, 当手指点击屏幕时函数的调用栈

  • 能够看到屏幕触摸事件是Source0处理的

8、监听RunLoop状态

  • 咱们能够经过给RunLoop添加Observer的方式监听RunLoop的状态, 使用下面这个函数
/** 给RunLoop添加Observer @param rl 目标RunLoop @param observer 须要添加的Observer @param mode 监听状态 */
void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFRunLoopMode mode);
复制代码
  • 能够经过下面的函数建立Observer
/**
 建立Observer

 @param allocator 分配器
 @param activities 须要监听的状态
 @param repeats 是否重复监听
 @param order 顺序
 @param callout 回调函数
 @param context 附加对象
 @return 建立好的Observer
 */
CF_EXPORT CFRunLoopObserverRef CFRunLoopObserverCreate(CFAllocatorRef allocator, CFOptionFlags activities, Boolean repeats, CFIndex order, CFRunLoopObserverCallBack callout, CFRunLoopObserverContext *context);
复制代码
  • RunLoop的状态有下面几种
/* Run Loop Observer Activities */
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
};
复制代码

一、监听点击事件

  • 建立一个Observer, 并将其添加到Runloop中, 监听屏幕点击事件

  • 运行程序就能够看到正在监听RunLoop的状态, 当最后没有事件时, Runloop进入睡眠状态

  • 清空打印, 点击屏幕, 有以下结果, 能够看到先进入sources状态, 而后执行点击事件

二、监听滚动事件

  • 监听UITextView的滚动, 查看RunLoopcurrentMode性能

  • 建立Observer的另外一种方式, 使用block监听RunLoop状态ui

  • view上添加一个UITextView

  • 运行程序, 滚动UITextView, 有以下打印

  • 能够看到滚动以前, 先退出kCFRunLoopDefaultMode, 进入UITrackingRunLoopMode
  • 等滚动结束, 会先退出UITrackingRunLoopMode, 进入kCFRunLoopDefaultMode
相关文章
相关标签/搜索