ios开发中的runloop
2016-06-14 19:42
295 查看
</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 // 定时器的精度 ); */
CFRunLoopSourceRef
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 = 0x0 bdb5 FFFFFFFU // 表示监听上面所有的状态 };
那么如何监听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处理逻辑
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
相关文章推荐
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- 讲解iOS开发中基本的定位功能实现
- iOS中定位当前位置坐标及转换为火星坐标的方法
- js判断客户端是iOS还是Android等移动终端的方法
- iOS应用开发中AFNetworking库的常用HTTP操作方法小结
- iOS应用中UISearchDisplayController搜索效果的用法
- iOS App开发中的UISegmentedControl分段组件用法总结
- IOS开发环境windows化攻略
- iOS应用中UITableView左滑自定义选项及批量删除的实现
- iOS中UIAlertView警告框组件的使用教程
- 浅析iOS应用开发中线程间的通信与线程安全问题
- iOS中的UIKeyboard键盘视图使用方法小结
- 检测iOS设备是否越狱的方法
- .net平台推送ios消息的实现方法