Ios 是否可以检查主线程是否空闲/是否可以排空主运行循环?

Ios 是否可以检查主线程是否空闲/是否可以排空主运行循环?,ios,objective-c,runloop,Ios,Objective C,Runloop,我刚刚阅读了下面的帖子,并尝试实现其中描述的方法: 这里描述的所有东西都非常有效。但是当我进行验收测试时,有一件事打破了决定论 这是Github上的repo,文章作者在这里推动了他的实验(可以在评论页面底部找到): 考虑一下他用来点击视图的代码: - (void) tapViewViaSelector:(NSString *)viewSelector{ [UIAutomationBridge tapView:[self viewViaSelector:viewSelector]];

我刚刚阅读了下面的帖子,并尝试实现其中描述的方法:

这里描述的所有东西都非常有效。但是当我进行验收测试时,有一件事打破了决定论

这是Github上的repo,文章作者在这里推动了他的实验(可以在评论页面底部找到):

考虑一下他用来点击视图的代码:

- (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;
    }
};