簡體   English   中英

是否可以檢查主線程是否處於空閑狀態/排干主運行循環?

[英]Is it possible to check that main thread is idle / to drain a main run loop?

我剛剛閱讀了以下帖子,並嘗試實現此處描述的方法:

使用Kiwi編寫iOS驗收測試-敏捷

那里描述的所有東西都可以正常工作。 但! 當我運行驗收測試時,有一件事破壞了確定性。

這是Github上的回購,文章的作者在其中進行了他的實驗(可以在評論的頁面底部找到): https : //github.com/moredip/2012-Olympics-iOS--iPad-and -iPhone--源代碼/樹/獼猴桃接受-mk1

考慮一下他用於點擊視圖的以下代碼:

- (void) tapViewViaSelector:(NSString *)viewSelector{
    [UIAutomationBridge tapView:[self viewViaSelector:viewSelector]];
    sleepFor(0.1); //ugh
}

...其中sleepFor具有以下定義

#define sleepFor(interval) (CFRunLoopRunInMode(kCFRunLoopDefaultMode, interval, false))

這是一個幼稚的嘗試(“幼稚”不是關於作者,而是關於它是進入頭腦的第一件事),要等待一小段時間,直到處理完所有動畫並吸收所有可能的時間為止。已(或可能)安排到主運行循環的事件(另請參見此評論 )。

問題在於,這種幼稚的代碼無法以確定性的方式工作。 大量的UI交互導致在當前編輯的文本字段的鍵盤消失之前按下FX的下一個按鈕,依次類推...

如果我將時間從0.1增加到fx 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。

要檢查與線程關聯的運行循環的狀態並注冊單獨階段的回調,可以使用CFRunLoopObserverRef 這樣可以對調用回調的時間進行非常精細的控制。 另外,您不必依賴於hacky超時等。

可以這樣添加一個(注意,我要在主運行循環中添加一個)

CFRunLoopObserverRef obs = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, true, 0 /* order */, handler);
CFRunLoopAddObserver([NSRunLoop mainRunLoop].getCFRunLoop, obs, kCFRunLoopCommonModes);
CFRelease(obs);

根據您注冊的活動,您的處理程序將被適當調用。 在上面的示例中,觀察者監聽所有活動。 您可能只需要kCFRunLoopBeforeWaiting

您的處理程序可能看起來像這樣

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

這是我當前的解決方案,如果沒人告訴我我錯了或者建議先給出一個更好的答案,我將在代碼中添加一些注釋和解釋。

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

現在我還不知道如何使它更有效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM