Ios 是否可以检查主线程是否空闲/是否可以排空主运行循环?
我刚刚阅读了下面的帖子,并尝试实现其中描述的方法: 这里描述的所有东西都非常有效。但是当我进行验收测试时,有一件事打破了决定论 这是Github上的repo,文章作者在这里推动了他的实验(可以在评论页面底部找到): 考虑一下他用来点击视图的代码:Ios 是否可以检查主线程是否空闲/是否可以排空主运行循环?,ios,objective-c,runloop,Ios,Objective C,Runloop,我刚刚阅读了下面的帖子,并尝试实现其中描述的方法: 这里描述的所有东西都非常有效。但是当我进行验收测试时,有一件事打破了决定论 这是Github上的repo,文章作者在这里推动了他的实验(可以在评论页面底部找到): 考虑一下他用来点击视图的代码: - (void) tapViewViaSelector:(NSString *)viewSelector{ [UIAutomationBridge tapView:[self viewViaSelector:viewSelector]];
- (void) tapViewViaSelector:(NSString *)viewSelector{
[UIAutomationBridge tapView:[self viewViaSelector:viewSelector]];
sleepFor(0.1); //ugh
}
…其中,sleepFor
具有:
这是一种幼稚的尝试(“幼稚”不是关于作者,而是关于这样一个事实,即它是第一件进入大脑的事情),等待一小段时间,直到所有动画都被处理,并浸泡所有可能的事件,这些事件已经(或可能)安排到主运行循环中(另请参见)
问题是,这种幼稚的代码不能以确定性的方式工作。在当前编辑的文本字段的键盘消失之前,有一系列的UI交互会导致按下fx下一步按钮,等等
如果我只是将时间从0.1增加到fx 1,所有问题都会消失,但这会导致每一次交互,如“用文本填充文本字段…”或“用标题点击按钮…”花费1秒
所以我的意思不是增加等待时间,而是一种让这种人工等待保证我可以继续下一步测试用例的方法
我希望这应该是一种更可靠的方式,等待当前操作(所有的转换/动画或任何主运行循环)引起的所有事情都完成
总而言之,这是一个问题:
是否有方法排出/排出/浸泡主线程及其运行循环中计划的所有内容,以确保主线程空闲且其运行循环为“空”
这是我最初的解决方案:
// DON'T like it
static inline void runLoopIfNeeded() {
// https://developer.apple.com/library/mac/#documentation/CoreFOundation/Reference/CFRunLoopRef/Reference/reference.html
while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES) == kCFRunLoopRunHandledSource);
// DON'T like it
if (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES) == kCFRunLoopRunHandledSource) runLoopIfNeeded();
}
你可以试试这个
while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == kCFRunLoopRunHandledSource);
这将一直运行,直到运行循环中没有其他内容为止。如果0不起作用,您可以尝试将时间间隔更改为0.1。这是我当前的解决方案,如果没有人告诉我我错了,或者建议先给出更好的答案,我会在稍后对代码添加一些注释和解释:
// It is much better, than it was, but still unsure
static inline void runLoopIfNeeded() {
// https://developer.apple.com/library/mac/#documentation/CoreFOundation/Reference/CFRunLoopRef/Reference/reference.html
__block BOOL flag = NO;
// http://stackoverflow.com/questions/7356820/specify-to-call-someting-when-main-thread-is-idle
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
flag = YES;
});
});
while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES) == kCFRunLoopRunHandledSource);
if (flag == NO) runLoopIfNeeded();
}
现在我不知道如何使这更有效。要检查与线程关联的运行循环的状态并注册单独阶段的回调,可以使用
CFRunLoopObserverRef
。这允许对调用回调的时间进行极细粒度的控制。此外,你不必依赖于黑客超时等
可以这样添加一个(注意,我正在向主运行循环添加一个)
根据您注册的活动,您的处理程序将被适当调用。在上面的示例中,观察者监听所有活动。在等待之前,您可能只需要kcfrunloop
你可能看起来像这样
id handler = ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
// About to enter the processing loop. Happens
// once per `CFRunLoopRun` or `CFRunLoopRunInMode` call
break;
case kCFRunLoopBeforeTimers:
case kCFRunLoopBeforeSources:
// Happens before timers or sources are about to be handled
break;
case kCFRunLoopBeforeWaiting:
// All timers and sources are handled and loop is about to go
// to sleep. This is most likely what you are looking for :)
break;
case kCFRunLoopAfterWaiting:
// About to process a timer or source
break;
case kCFRunLoopExit:
// The `CFRunLoopRun` or `CFRunLoopRunInMode` call is about to
// return
break;
}
};
谢谢你的回答。这是我最初的解决方案,在我写这篇文章之前。我将在几分钟后发布我当前的一个。我真的很高兴我们朝一个方向思考!我用我的第一次尝试更新了我的问题,所以你可以抓住我最初的想法和它是如何演变的(希望如此)/cc@xlcI看不到
dispatch\u async
的好处,您应该尝试将其作为while循环,而不是使用递归dispatch\u async的好处是确保在调用runLoopIfNeeded()之前调度到主队列的任何块都首先被清空。我的推理是否有误?while循环CFRunLoopRunInMode
应该可以确保这一点。唯一的问题是,有些甚至可能有延迟,你不知道延迟时间。e、 动画完成处理程序将在动画持续时间后调用,因此您必须等待动画持续时间。是的,我知道这些情况。为此,我使用了specialhelperfinally(),比如[[值(最终(^{return hasLabelWithText(@“Some text”);})应该]beYes];此finally()帮助程序基于runLoopIfNeeded()。
CFRunLoopObserverRef obs = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, true, 0 /* order */, handler);
CFRunLoopAddObserver([NSRunLoop mainRunLoop].getCFRunLoop, obs, kCFRunLoopCommonModes);
CFRelease(obs);
id handler = ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
// About to enter the processing loop. Happens
// once per `CFRunLoopRun` or `CFRunLoopRunInMode` call
break;
case kCFRunLoopBeforeTimers:
case kCFRunLoopBeforeSources:
// Happens before timers or sources are about to be handled
break;
case kCFRunLoopBeforeWaiting:
// All timers and sources are handled and loop is about to go
// to sleep. This is most likely what you are looking for :)
break;
case kCFRunLoopAfterWaiting:
// About to process a timer or source
break;
case kCFRunLoopExit:
// The `CFRunLoopRun` or `CFRunLoopRunInMode` call is about to
// return
break;
}
};