网上关于runloop的解析已经更丰富了,这里只记录runloop的实践部分
RunLoop
干嘛的
为了实现线程不退出,可以随时接受消息执行任务,
node.js 的事件处理,windows程序的消息循环,iOS、OSX的RunLoop都是这种机制
线程和runloop一一对应,关系保存在全局字典中
主线程自带runloop,无需创建,新建的线程需要手动对应runloop,不让执行完成就结束了
常驻线程
常驻线程的意义,我理解是用来处理需要在子线程长期处理的事情,比如说记日志,埋点,IO数据处理等等之类。
这样想的话其实和NSOperationqueue或者GCD实现一个串行队列应该没有什么区别。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| static NSThread *workThread;
-(void)initThread{
workThread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[workThread start]; }
// 让子线程runloop跑起来,防止线程结束 -(void)run{
[[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; while (!_stopRunning) { @autoreleasepool { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } } }
// 这段代码直接复制自weex // 在子线程执行代码 + (void)_performBlockOnBridgeThread:(void (^)(void))block { if ([NSThread currentThread] == workThread) { block(); } else { [self performSelector:@selector(_performBlockOnBridgeThread:) onThread:[self jsThread] withObject:[block copy] waitUntilDone:NO]; } }
|
runloop observer
可以通过CFRunLoopAddObserver,给runloop添加观察者,具体实践如下
这是CDChatList中的一段代码,主要是为了实现,label在scrollview滚动时,去除选中文字的样式,如果不用runloop也是可以用通知或者其他的代理形式实现,但是会有一定的耦合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| currentMode = CFRunLoopCopyCurrentMode(CFRunLoopGetMain());
__weak typeof(self) weakS = self; // 这里监听了所有的runloop事件,然后在回调中过滤出滚动事件,因为滚动事件会一直回调,所以这里需要特别处理,只观察进入滚动的时机 CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { if (weakS) { __strong typeof(weakS) strongS = weakS; CFComparisonResult rest = CFStringCompare(strongS->currentMode, CFRunLoopCopyCurrentMode(CFRunLoopGetMain()), kCFCompareBackwards); if (rest != kCFCompareEqualTo) { strongS->currentMode = CFRunLoopCopyCurrentMode(CFRunLoopGetMain()); if ((NSString *)CFBridgingRelease(strongS->currentMode) == UITrackingRunLoopMode) { [strongS scrollDidScroll]; } } } });
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
|
下面是YYTransaction中相关的代码,具有类似的实现,这里监听的目的应该是在runloop将要进入休眠时,把不需要立即执行的任务执行,以达到async的效果。
在AsyncDisplayKit中也有类似的用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| static NSMutableSet *transactionSet = nil;
static void YYRunLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) { if (transactionSet.count == 0) return; NSSet *currentSet = transactionSet; transactionSet = [NSMutableSet new]; [currentSet enumerateObjectsUsingBlock:^(YYTransaction *transaction, BOOL *stop) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [transaction.target performSelector:transaction.selector]; #pragma clang diagnostic pop }]; }
static void YYTransactionSetup() { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ transactionSet = [NSMutableSet new]; CFRunLoopRef runloop = CFRunLoopGetMain(); CFRunLoopObserverRef observer; observer = CFRunLoopObserverCreate(CFAllocatorGetDefault(), kCFRunLoopBeforeWaiting | kCFRunLoopExit, true, // repeat 0xFFFFFF, // after CATransaction(2000000) YYRunLoopObserverCallBack, NULL); CFRunLoopAddObserver(runloop, observer, kCFRunLoopCommonModes); CFRelease(observer); }); }
|