iOS RunLoop 基本概念以及使用场景

来源:互联网 发布:微创拔牙多少钱 知乎 编辑:程序博客网 时间:2024/05/18 09:09
一、RunLoop概念:
一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出。如果我们需要一个机制,让线程能随时处理事件但并不退出。
实现这种模型的关键点在于:如何管理事件/消息,如何让线程在没有处理消息时休眠以避免资源占用、在有消息到来时立刻被唤醒。
所以,RunLoop 实际上就是一个对象,这个对象管理了其需要处理的事件和消息,并提供了一个入口函数来执行上面 Event Loop 的逻辑。线程执行了这个函数后,就会一直处于这个函数内部 "接受消息->等待->处理" 的循环中,直到这个循环结束(比如传入 quit 的消息),函数返回。
二、RunLoop与线程的关系
首先,iOS 开发中能遇到两个线程对象: pthread_t 和 NSThread。过去苹果有份文档标明了 NSThread 只是 pthread_t 的封装,但那份文档已经失效了,现在它们也有可能都是直接包装自最底层的 mach thread。苹果并没有提供这两个对象相互转换的接口,但不管怎么样,可以肯定的是 pthread_t 和 NSThread 是一一对应的。比如,你可以通过 pthread_main_thread_np() 或 [NSThread mainThread] 来获取主线程;也可以通过 pthread_self() 或 [NSThread currentThread] 来获取当前线程。CFRunLoop 是基于 pthread 来管理的。
注意:苹果不允许直接创建 RunLoop,它只提供了两个自动获取的函数:CFRunLoopGetMain() 和 CFRunLoopGetCurrent()。 这两个函数内部的逻辑大概是下面这样
RunLoop 的核心就是一个 mach_msg() ,RunLoop 调用这个函数去接收消息,如果没有别人发送 port 消息过来,内核会将线程置于等待状态。例如你在模拟器里跑起一个 iOS 的 App,然后在 App 静止时点击暂停,你会看到主线程调用栈是停留在 mach_msg_trap() 这个地方。
自动释放池就是根据runloop的原理实现的
回到开始的疑问,为什么要使用RunLoop,一般情况下我们是没必要去启动线程的RunLoop,除非需要在一个单独的线程长久的检测某个事件,类似微信的语音功能,见一个RunLoop专门负责监听说话的线程。看需求而定了。
1.每条线程都有唯一的一个与之对应的RunLoop对象
2.主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建
3.RunLoop在第一次获取时创建,在线程结束时销毁
4.获取RunLoop对象:
1)Foundation:
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
2)Core Foundation:
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象
三、RunLoop使用场景
苹果官方文档说明run loop的开启是运用在需要和线程有更多交互的场合上的。
四、概念
1)Foundation 框架          ——>NSRunLoop
2)Core Foundation         ——>CFRunLoopRef
NSRunLoopCFRunLoopRef都代表着RunLoop对象
NSRunLoop是基于CFRunLoopRef的一层OC包装, 所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API (Core Foundation 层面)
五、RunLoop相关类
 CoreFoundation中关于RunLoop的5个类
 1)CFRunLoopRef           ——>NSRunLoop是基于这个类进行封装的
 2)CFRunLoopModeRef  ——>代表RunLoop的运行模式
一个 RunLoop包含若干个 Mode,每个Mode又包含若干个Source/Timer/Observer
每次RunLoop启动时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode
如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入,这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响
系统默认注册了5个Mode:
kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView追踪触摸滑动,保证界面滑动时不受其他Mode 影响
UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode
 3)CFRunLoopSourceRef
 4)CFRunLoopTimerRef
 5)CFRunLoopObserverRef
六、RunLoop应用
 1.NSTimer
 2.ImageView显示
 3.PerformSelector
 4.常驻线程
 5.自动释放池
#import "ViewController.h"@interface ViewController ()@property (nonatomic, strong) dispatch_source_t timer;@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    // Do any additional setup after loading the view, typically from a nib.}- (void)didReceiveMemoryWarning {    [super didReceiveMemoryWarning];    // Dispose of any resources that can be recreated.}- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event  {//    // NSRunLoop 主线程对应的RunLoop对象//    NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop];//    NSLog(@"mainRunLoop = %@", mainRunLoop);//     // NSRunLoop 获得当前方法所在线程对应的RunLoop//    NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];//    NSLog(@"currentRunLoop = %@", currentRunLoop);//    // CFRunLoopRef 主线程对应的RunLoop对象//    CFRunLoopRef cfMainRunLoop = CFRunLoopGetMain();//    NSLog(@"cfMainRunLoop = %@", cfMainRunLoop);//    // CFRunLoopRef 获得当前方法所在线程对应的RunLoop//    CFRunLoopRef cfCurrentRunLoop = CFRunLoopGetCurrent();//    NSLog(@"cfCurrentRunLoop = %@", cfCurrentRunLoop);//    // 开启一条子线程//    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];//    [thread start];        // 创建Observer    /**     *  参数1: 指定如果给Observer分配存储空间     *  参数2: 需要监听的状态类     *       kCFRunLoopEntry = (1UL << 0),         即将启动(进入)的时候     *       kCFRunLoopBeforeTimers = (1UL << 1),  即将处理timer事件     *       kCFRunLoopBeforeSources = (1UL << 2), 即将处理source事件     *       kCFRunLoopBeforeWaiting = (1UL << 5), 即将进入睡眠     *       kCFRunLoopAfterWaiting = (1UL << 6),  RunLoop被唤醒     *       kCFRunLoopExit = (1UL << 7),          RunLoop退出     *       kCFRunLoopAllActivities = 0x0FFFFFFFU 监听所有状态     *     */    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {        switch (activity) {            case kCFRunLoopEntry:                NSLog(@"即将进入RunLoop");                break;            case kCFRunLoopBeforeTimers:                NSLog(@"即将处理timer");                break;            case kCFRunLoopBeforeSources:                NSLog(@"即将处理source");                break;            case kCFRunLoopBeforeWaiting:                NSLog(@"即将进入睡眠");                break;            case kCFRunLoopAfterWaiting:                NSLog(@"RunLoop刚从睡眠中唤醒");                break;            case kCFRunLoopExit:                NSLog(@"RunLoop即将退出");                break;            default:                break;        }    });        // 给主线程的RunLoop添加一个观察者,要监听的是RunLoop的哪种运行模式    /**     *  参数1: 需要给哪个RunLoop添加观察者     *  参数2: 需要添加的Observer对象     *  参数3: 在哪种模式下可以监听 kCFRunLoopDefaultMode == NSDefaultRunLoopMode     */    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode);    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES];}- (void)show{    NSLog(@"-------------%s", __func__);}- (void)run{    // 注意: 如果想给子线程添加RunLoop, 不能直接alloc init    //    [[NSRunLoop alloc] init]; // 错误    // 只要调用currentRunLoop方法, 系统就会自动创建一个RunLoop, 添加到当前线程中    [NSRunLoop currentRunLoop]; // 这个方法是懒加载}



0 0
原创粉丝点击