您的位置:首页 > 移动开发 > IOS开发

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

2017-05-04 09:50 369 查看
一、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

NSRunLoop和CFRunLoopRef都代表着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]; // 这个方法是懒加载
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ios objective-c RunLoop
相关文章推荐