配置Runloop的sources
当系统的输入源不足以满足我们的需求的时候, 我们能自己设置输入源. 看了苹果的官方文档, 也没有知道为什么, 所以妄自猜测下, 可可以是当系统的输入源不足以满足我们的需求的时候, 我们需要自己设置输入源.
定义输入源
网上大部分配置sources的demo, 核心代码都出自这里, 下面简单的对自己设置source的类图进行分析.
自己设置输入源.png
核心类是CCRunLoopInputSource
也就是自己设置的输入源. CCRunLoopCustomInputSourceThread
是自己设置输入源生存的环境.
创立自己设置输入源需要定义以下内容
- 1 输入源要解决的信息.
- 2 输入源被增加到Runloop时的调度例程.
- 3 输入源被告知有事件要解决的调度例程.
- 4 输入源被取消时的调度例程.
typedef struct { CFIndex version; void * info; const void *(*retain)(const void *info); void (*release)(const void *info); CFStringRef (*copyDescription)(const void *info); Boolean (*equal)(const void *info1, const void *info2); CFHashCode (*hash)(const void *info); void (*schedule)(void *info, CFRunLoopRef rl, CFRunLoopMode mode); void (*cancel)(void *info, CFRunLoopRef rl, CFRunLoopMode mode); void (*perform)(void *info);} CFRunLoopSourceContext;// CCRunLoopInputSource.m- (instancetype)init{ self = [super init]; if (self) { CFRunLoopSourceContext context = {0, (__bridge void *)(self), NULL, NULL, NULL, NULL, NULL, &runLoopSourceScheduleRoutine, &runLoopSourceCancelRoutine, &runLoopSourcePerformRoutine}; _runLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); _commands = [NSMutableArray array]; } return self;}
在CFRunLoopSourceCreate
创立输入源的时候, 需要传入CFRunLoopSourceContext
结构体指针, 这里第二个参数的info
传的是self
, 当系统回调时候会把这个信息当成上下文会调, 此外还定义了schedule
, perform
, cancel
三个函数指针, 分别对应事件源被加到Runloop, 输入源被告知有事件要解决, 和输入源失效的函数回调.
当调使用以下方法
- (void)addToCurrentRunLoop{ CFRunLoopRef runLoop = CFRunLoopGetCurrent(); CFRunLoopAddSource(runLoop, _runLoopSource, kCFRunLoopDefaultMode);}
会导致runLoopSourceScheduleRoutine
函数回调,
void runLoopSourceScheduleRoutine (void *info, CFRunLoopRef runLoopRef, CFStringRef mode){ CCRunLoopInputSource *runLoopInputSource = (__bridge CCRunLoopInputSource *)info; CCAppDelegate *appDelegate = [UIApplication sharedApplication].delegate; CCRunLoopContext *runLoopContext = [[CCRunLoopContext alloc] initWithSource:runLoopInputSource runLoop:runLoopRef]; [appDelegate performSelectorOnMainThread:@selector(registerSource:) withObject:runLoopContext waitUntilDone:NO];}
这里只是把回调的事件源封装成CCRunLoopContext
再抛出去, 这里是抛给CCAppDelegate
, 实际上抛给哪个类解决都是能的.
当执行
- (void)fireAllCommandsOnRunLoop:(CFRunLoopRef)runLoop{ NSLog(@"Current Thread: %@", [NSThread currentThread]); CFRunLoopSourceSignal(_runLoopSource); CFRunLoopWakeUp(runLoop);}
实际上是把当前事件源标记为有事件要解决, 而后调使用CFRunLoopWakeUp
唤起线程, 相似我们的
[self.view setNeedsLayout]; [self.view layoutIfNeeded];
将当前画布标记为dirty
, 而后触发重绘.
线程被wakeup后, 会执行上一篇博文里面步骤9, 解决source1和timer, 这里当然就是source1了,
void runLoopSourcePerformRoutine (void *info){ CCRunLoopInputSource *runLoopInputSource = (__bridge CCRunLoopInputSource *)info; [runLoopInputSource inputSourceFired];}
实际是执行了runLoopSourcePerformRoutine
回调, 这里我们看到只是将回调传递来的事件源info
取出来, 并执行inputSourceFired
- (void)inputSourceFired{ NSLog(@"Enter inputSourceFired"); // Test if (_testPrintString) { if ([self.delegate respondsToSelector:@selector(activeInputSourceForTestPrintStringEvent:)]) { [self.delegate activeInputSourceForTestPrintStringEvent:_testPrintString]; } } NSLog(@"Exit inputSourceFired");}
这里调使用了代理商方法activeInputSourceForTestPrintStringEvent
, 将, 最终CCRunLoopInputSource
的代理商CCRunLoopCustomInputSourceThread
将解决这个事件.
- (void)activeInputSourceForTestPrintStringEvent:(NSString *)string{ NSLog(@"activeInputSourceForTestPrintStringEvent : %@", string);}
当然这里的代理商不肯定要是CCRunLoopCustomInputSourceThread
, 也能是其它的类.
当调使用
- (void)invalidate{ CFRunLoopRef runLoop = CFRunLoopGetCurrent(); CFRunLoopRemoveSource(runLoop, _runLoopSource, kCFRunLoopDefaultMode);}
使一个事件源失效的时候, 会触发runLoopSourceCancelRoutine
回调
void runLoopSourceCancelRoutine (void *info, CFRunLoopRef runLoopRef, CFStringRef mode){ CCRunLoopInputSource *runLoopInputSource = (__bridge CCRunLoopInputSource *)info; CCAppDelegate *appDelegate = [UIApplication sharedApplication].delegate; CCRunLoopContext *runLoopContext = [[CCRunLoopContext alloc] initWithSource:runLoopInputSource runLoop:runLoopRef]; [appDelegate performSelectorOnMainThread:@selector(removeSource:) withObject:runLoopContext waitUntilDone:YES];}
下面看下AppDelegate的代码
@implementation CCAppDelegate (RunLoop)- (void)registerSource:(CCRunLoopContext *)sourceContext{ if (!self.sources) { self.sources = [NSMutableArray array]; } [self.sources addObject:sourceContext];}- (void)removeSource:(CCRunLoopContext *)sourceContext{ [self.sources enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { CCRunLoopContext *context = obj; if ([context isEqual:sourceContext]) { [self.sources removeObject:context]; *stop = YES; } }];}- (void)testInputSourceEvent{ CCRunLoopContext *runLoopContext = [self.sources objectAtIndex:0]; CCRunLoopInputSource *inputSource = runLoopContext.runLoopInputSource; [inputSource addTestPrintCommandWithString:[[NSDate date] description]]; [inputSource fireAllCommandsOnRunLoop:runLoopContext.runLoop];}@end
这里维护了一个输入源
的数组, 使用来区分不同的输入源. 对于不同的输入源我们能在testInputSourceEvent
选择不同的输入源进行触发.
[inputSource addTestPrintCommandWithString:[[NSDate date] description]];
上面的方法, 我了解是进行数据的传递, 作者也设计了更加通使用的接口
- (void)addCommand:(NSInteger)command data:(NSData *)data;
不过demo里面没有用到.
这里实际上是建立了一个通道和线程状态切换的机制
自己设置输入源数据通道.png
臆想:
这个通道是线程之间传递数据, 唤醒休眠线程的一套机制, 我们完全能自己设置一个输入源, 使用来解决服务器发来的数据.
步骤如下:
- 1 初始化线程的时候创立输入source, 并增加.
- 2 当有数据过来的时候调使用
CFRunLoopSourceSignal
和CFRunLoopWakeUp
, 并将当前数据的回调板块信息保存到CFRunLoopSourceSignal
. - 3 在子线程里进行数据解析等少量耗时操作.
- 4 当数据解析完, 将数据封装成
CCRunLoopContext
, 找到回调板块, 在主线程进行回调(performSelectorOnMainThread
).
相比照dispatch_async
这样做的好处是, 在触发事件前, 能保存少量数据到自己设置source, 这样在回调的时候能很方便的找到指定的方法进行回调, 有点相似运行时的效果.
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 配置Runloop的sources