_嵌入式开发_

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

NSRunloop的坑

转自:

iOS中Run Loop的那些坑 http://www.hrchen.com/2013/07/tricky-runloop-on-ios/

前段时间写了个关于iOS多线程编程的系列:

iOS多线程编程Part 1/3 - NSThread & Run Loop中讨论了Run Loop的机制、接口和需要注意的坑,不过由于内容较多,描述Run Loop的坑写得很零散,本文做一个集中归纳。

回顾一下,Run Loop就是个监听事件的循环,会不停的检查它的事件源(Timer和Input Source)有没有事件发生,如果有事件发生就处理事件或者调用事件的处理方法。

坑一

在线程中添加Timer时,肯定需要先生成Timer对象啦,有类方法也有实例方法,如果是使用scheduledTimerWith*接口生成的Timer对象,会自动添加到当前线程的NSDefaultRunLoopMode中;如果是其他接口生成的Timer对象,则需要用-addTimer:forMode添加Timer,这样做的好处是可以指定添加Timer的Run Loop以及模式。

坑二

如果Run Loop中添加的是Timer而没有其他Input Source,而这个Timer只运行一次,那么Timer事件触发后Timer事件源就会从Run Loop删除,那么再运行Run Loop就会立刻返回;同时Timer事件触发是不会让Run Loop返回的,即使使用CF层的CFRunLoopRef运行接口SInt32 CFRunLoopRunInMode (mode, seconds, returnAfterSourceHandled);运行Run Loop,其第三个参数为YES,Timer事件触发仍然不会导致当前Run Loop的运行返回。

坑三

如果是使用NSRunLoop,有三个运行的接口:

//运行 NSRunLoop,运行模式为默认的NSDefaultRunLoopMode模式,没有超时限制
- (void)run;

//运行 NSRunLoop: 参数为运行模式、时间期限,返回值为YES表示是处理事件后返回的,NO表示是超时或者停止运行导致返回的。[Update]: 感谢网友olo的提醒:返回值只有在RunLoop没有运行的情况下才返回NO。比如:1)没有添加输入源和定时器源 2)mode为NSRunLoopCommonModes 或UITrackingRunLoopMode 等“非法”参数。如果超时的话返回YES,即使limitDate的初始值小于当前的Date,RunLoop也会执行一次然后马上返回YES。
- (BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;

//运行 NSRunLoop: 参数为运时间期限,运行模式为默认的NSDefaultRunLoopMode模式
- (void)runUntilDate:(NSDate *)limitDate;

建议是使用第二个接口来运行,因为它能够设置Run Loop的运行参数最多,而且最重要的是可以使用CFRunLoopStop(runLoopRef)来停止Run Loop的运行,而第一个和第三个接口无法使用CFRunLoopStop(runLoopRef)来停止Run Loop的运行。

坑四

在使用NSURLConnection或者NSStream时,需要考虑到Run Loop的问题,默认情况下这两个对象生成后都是运行在当前线程的NSDefaultRunLoopMode模式的,如果是在主线程,那么在滚动ScrollView或者TableView时,主线程的Run Loop会运行在UITrackingRunLoopMode模式,那么NSURLConnection或者NSStream的回调就无法运行。因此最好是指定NSURLConnection或NSStream在Run Loop中的运行模式,两者有相同的接口- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;来设置NSURLConnection和NSStream的Run Loop以及模式,而且设置的Mode要设置为NSRunLoopCommonModes,因为NSRunLoopCommonModes默认会包含NSDefaultRunLoopMode和UITrackingRunLoopMode,这样无论Run Loop运行在哪个模式,都可以保证NSURLConnection或者NSStream的回调可以被调用。另外如果是在子线程中你设置了自定义的Run Loop模式,还可以用接口CFRunLoopAddCommonMode(runLoopRef, mode)添加到NSRunLoopCommonModes。

不过NSURLConnection的使用有点特殊,必须使用它的designated initializer - (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately生成NSURLConnection对象,而且第三个参数是否立刻启动NSURLConnection要设置为NO,之后再用- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;设置Run Loop与模式,再调用[NSURLConnectionObject start]启动。如果是其他接口生成NSURLConnection,用- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;设置Run Loop和mode都不会起作用。

posted on 2015-07-05 21:10  _嵌入式开发_  阅读(99)  评论(0)    收藏  举报