ios开发中的runloop
来源:互联网 发布:win10提速 优化 编辑:程序博客网 时间:2024/05/22 11:31
</pre>一直觉得ios中的runloop特别重要,想要理解一下,然后在开发过程中希望能够用runloop在某些地方写些跟优雅的代码。<p></p><p>在网上看了些资料,自己总结如下吧!</p><p>很早以前,我研究过windows编程,其中就认识到原来应用可以一直处于运行状态是因为里边有一个while死循环,让应用根本没办法从main入口函数中退出!真是人才,也很容易理解这一点吧!而这样的情况放在ios中就是我们接下来要讲的runloop。</p><p>runloop的作用:</p><p>1.保持程序不会退出</p><p style="text-align:justify">2.程序不退出,就可以随时处理app中的各种事件,包括人为的事件,定时器事件,selector事件等等</p><p style="text-align:justify">3.节省cup资源,提高程序的性能,有事情做的时候就唤醒,没事情做的时候就睡眠状态</p><p style="text-align:justify"></p><pre name="code" class="objc">int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); }}其中UIApplicationMain这个函数就有一个Runloop,这个Runloop就是主线程中的Runloop,所以主线程才一直不会退出,所以应用也不会结束.
Foundation框架中的NSRunLoop与Core Foundation中的CFRunLoop
RunLoop与线程有直接的非常不同寻常的关系,形影不离
1.没一条线程都有一个唯一的与之对应的RunLoop对象
2.主线程的RunLoop是由系统自己创建的,然后子线程的RunLoop对象我们如果要使用就必须自己去创建
3.一个线程结束了,那么这个线程所对应的RunLoop对象就会销毁
下面来分析下RunLoop的结构
一个RunLoop中可以拥有多个不同的Mode,但是RunLoop在同一时间运行只能去使用其中某一个Mode,如果要切换Mode,必须先退出之前的那个Mode,才能进入新的Mode中。
这里所说的Mode,其实就是指RunLoop的不同的运行模式,它有五种运行模式,如下
1.NSDefaultRunLoopMode 主线程的RunLoop就是在这种模式下运行
2.UITrackingRunLoopMode 界面跟踪模式,当用户与界面交互的时候会在这种模式下运行RunLoop
3.NSRunLoopCommonModes 模式占位,上边的两种模式都可以运行RunLoop
4.UIInitializationRunLoopMode 程序启动时处于这个模式下,程序一点启动完毕就不会在这个模式下了
5.GSEventReceiveRunLoopMode 接受系统事件的内部模式
每个CFRunLoopModeRef中又包含多个Source,Timer,Observer,包含的这三个东东,其实就是去触发RunLoop唤醒不死线程的作用(Observer好像不是这个作用,我暂且就这么理解吧!),下边我们来一一讲解:
CFRunLoopTimerRef:
屁话不多说,其实就是个NSTimer
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];<span style="font-family: Arial, Helvetica, sans-serif;"> </span>
<span style="font-family: Arial, Helvetica, sans-serif;">//将创建的timer加入到DefaultRunLoopMode中,而这个timer在其他模式,比如TrackingRunLoopMode下会停止工作</span>
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
</pre><pre name="code" class="objc">
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES]; // 拖拽UI界面时出发定时器,在默认模式(NSDefaultRunLoopMode)下不工作 [[NSRunLoop mainRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES]; // NSRunLoopCommonModes仅仅是标记NSTimer在两种模式(UITrackingRunLoopMode/NSDefaultRunLoopMode)下都能运行,但一个RunLoop中同一时间内只能运行一种模式 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
// 默认已经添加到主线程中RunLoop(mainRunLoop)中(Mode:NSDefaultRunLoopMode) [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
</pre>看过别人写的简书的以为作者<a target=_blank target="_blank" class="author-name blue-link" href="http://www.jianshu.com/users/244aa1f48d1c" style="color:rgb(64,148,199); text-decoration:none; margin:0px 5px; font-family:'lucida grande','lucida sans unicode',lucida,helvetica,'Hiragino Sans GB','Microsoft YaHei','WenQuanYi Micro Hei',sans-serif; line-height:20px">YotrolZ </a>写过的一篇文章中讲到:<p></p><p style="text-align:justify">GCD中的定时器跟NSTimer计时器是不一样的,GCD定时器不受Mode的yin'xiang</p><p style="text-align:justify"></p><p style="text-align: justify;"></p><pre name="code" class="objc">/** 定时器对象 */@property (nonatomic, strong)dispatch_source_t timer; // 需要一个强引用
NSLog(@"开始"); // 获取队并发队列,定时器的回调函数将会在子线程中执行 // dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 获取主队列,定时器的回调函数将会在子线程中执行 dispatch_queue_t queue = dispatch_get_main_queue(); self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); // 该时间表示从现在开始推迟两秒 dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC); // 设置定时器的开始时间,间隔时间 dispatch_source_set_timer(self.timer, start, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC); dispatch_source_set_event_handler(self.timer, ^{ NSLog(@"------%@", [NSThread currentThread]); }); dispatch_resume(self.timer);/* 参数说明:// 设置定时器的一些属性 dispatch_source_set_timer(dispatch_source_t source, // 定时器对象 dispatch_time_t start, // 定时器开始执行的时间 uint64_t interval, // 定时器的间隔时间 uint64_t leeway // 定时器的精度 );*/
1.Port-based Sources : 内核相关
2.Custom Input Sources : 与自定义Sources相关
3.Cocoa Perform Selector Sources : 与self performSelector方法相关
4.Source0 : 非基于port
5. Source1 : 基于port
CFRunLoopObserverRef:
CFRunLoopObserverRef是RunLoop的观察者,可以通过CFRunLoopObserverRef来监听RunLoop的状态改变.
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) { kCFRunLoopEntry = (1UL << 0), // 状态值:1,表示进入RunLoop kCFRunLoopBeforeTimers = (1UL << 1), // 状态值:2,表示即将处理NSTimer kCFRunLoopBeforeSources = (1UL << 2), // 状态值:4,表示即将处理Sources kCFRunLoopBeforeWaiting = (1UL << 5), // 状态值:32,表示即将休眠 kCFRunLoopAfterWaiting = (1UL << 6), // 状态值:64,表示从休眠中唤醒 kCFRunLoopExit = (1UL << 7), // 状态值:128,表示退出RunLoop 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.将观察者添加到对应的RunLoop上
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);3.C语言中的对象要主动释放
CFRelease(observer);
RunLoop的具体使用
1.图片刷新(假如界面要刷新N多图片(渲染),此时用户拖拽UI控件就会出现卡的效果,我们可以通过RunLoop实现,只在RunLoop默认Mode下下载,也就是拖拽Mode下不刷新图片)
- (void)viewDidLoad { [super viewDidLoad]; // 只在NSDefaultRunLoopMode下执行(刷新图片) [self.myImageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"0"] afterDelay:2.0 inModes:@[NSDefaultRunLoopMode]]; }
2.保证一个线程永远不死
- 方案一:用一个强引用引用住线程(这种方案是不可行的),原因如下:
#import "ViewController.h"#import "YCThread.h"@interface ViewController ()/*思路:用一个强引用线程,当点击屏幕的时候再让他启动,结果是不可行!!!!因为,线程执行完内部的任务后就会自动死亡,你如果用一个强引用引用这个线程,即使内存还在,但是该线程也已经处于死亡状态(线程状态),是不能再次启动的,如果再次启动一个死亡状态的线程,就会报错--reason: '*** -[YCThread start]: attempt(视图) to start the thread again'*//** 线程对象 */@property (nonatomic, strong)YCThread *thread; // 强引用@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; // 创建子线程 self.thread = [[YCThread alloc] initWithTarget:self selector:@selector(run) object:nil]; // 启动子线程 [self.thread start];}- (void)run { NSLog(@"----------");}- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // 点击屏幕再次启动线程 [self.thread start];}@end
- 方案二:(死循环+RunLoop),不建议此做法,不是太好
#import "ViewController.h"@interface ViewController ()/** 线程对象 */@property (nonatomic, strong)NSThread *thread;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; // 创建子线程 self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; [self.thread start];}- (void)run { NSLog(@"run--%@", [NSThread currentThread]); // 利用死循环(不建议此做法) while (1) { [[NSRunLoop currentRunLoop] run]; }}- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:YES modes:@[NSDefaultRunLoopMode]];}- (void)test { NSLog(@"test--%@", [NSThread currentThread]);}@end
方案三:(子线程中加入RunLoop+RunLoop源)建议采用此方案#import "ViewController.h"/*思路:为了保证线程不死,我们考虑在子线程中加入RunLoop, 但是由于RunLoop中没有没有源,就会自动退出RunLoop, 所以我们要为子线程添加一个RunLoop, 并且为这个RunLoop添加源(保证RunLoop不退出)*/@interface ViewController ()/** 线程对象 */@property (nonatomic, strong)NSThread *thread;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; // 创建子线程 self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; //启动子线程 [self.thread start];}- (void)run { NSLog(@"run--%@", [NSThread currentThread]); // 子线程 // 给子线程添加一个RunLoop,并且加入源 [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode]; // 启动RunLoop [[NSRunLoop currentRunLoop] run]; NSLog(@"------------"); // RunLoop启动,这句没有执行的机会}- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // 在子线程中调用test方法,如果子线程还在就能够调用成功 [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:YES modes:@[NSDefaultRunLoopMode]];}- (void)test { NSLog(@"test--%@", [NSThread currentThread]); // 子线程}@end
- ios开发中的runloop
- iOS中的RunLoop
- iOS中的Runloop
- iOS中的RunLoop
- iOS中的RunLoop
- 浅谈iOS中的RunLoop
- iOS开发 runloop&runtime
- iOS开发多线程-RunLoop
- iOS开发之Runloop
- iOS开发-RunLoop总结
- iOS开发之runloop
- ios高级开发----RunLoop
- iOS开发-RunLoop总结
- iOS开发-Runloop机制
- iOS开发之Runloop
- iOS中的runtime和runloop
- ios开发之Runloop简介
- iOS开发:深入理解RunLoop
- python的动态性以及 使用__slots__
- iOS 强制竖屏或是横屏 (代码设置后仍无效的问题)
- IOS通知和代理的用法以及优劣比较
- JSTL
- 栈和队列相关面试题(3)
- ios开发中的runloop
- Go语言gdb调试打印全局变量
- Python的第一个实用小程序--IDE下翻译
- English_study v
- HTML5 应用程序缓存
- 数据结构之红黑树
- IOS UIScrollView
- Android Studio创建app问题: Install repository and sync project等
- 类的静态成员函数带来了什么好处,应该在什么时候使用?