2016-04-18 5 views
7
let expecation = expectationWithDescription("do tasks") 
for i in 0...40 { 

    let afterTiming = 0.3 * Double(i) 
    let startTime = CFAbsoluteTimeGetCurrent() 

    let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(afterTiming * Double(NSEC_PER_SEC))) 

    dispatch_after(delayTime, dispatch_get_main_queue()) { 
     let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime 
     print("\(afterTiming) - \(timeElapsed) : \(i)") 
    } 
} 

waitForExpectationWithTimeout(14) 

nach 30 führt seine fast eine Sekunde aus, und Konsole mit, die zwei und zwei Drucklinien wirken seltsam beginnen gleichzeitigWie führe ich eine Aktion genau nach 0,3 Sekunden für eine bestimmte Anzahl von Malen?

9.0 - 9.88806998729706 : 30 
9.3 - 9.88832598924637 : 31 

Gibt es eine Möglichkeit für einen XCTest näher zu kommen, um tatsächlich die Anfragen zu tun " auf die richtige Zeit "? Wie die Anfrage bekommen, was getan werden soll, nach 9 Sekunden nicht nach 9,88 Sekunden getan ..

+1

Ich habe das 'XCTest'-Tag entfernt, da dies nichts mit XCTests zu tun hat. Es ist nur ein Zufall, dass Sie das für einen Test benötigen. Obwohl ... Ich würde gerne sehen, was Sie tatsächlich versuchen, so zu testen (und dann den Tag zurück hinzufügen). – nhgrif

+0

@nhgrif es ist wichtig, dass XCTest erwähnt wird, weil es tatsächlich waitForExpectationsWithTimeout() aufruft - ich bin mir nicht sicher, ob das funktionieren wird, zum Beispiel das Hauptthread in Tests zu blockieren – hakonbogen

+1

Wenn Sie 'XCTest' zu erwähnen, ist es wichtig, * muss wirklich den Test bereitstellen, den Sie für den Kontext ausführen möchten. So wie es jetzt aussieht, ist dies ein kleines [XY-Problem] (http://meta.stackexchange.com/q/66377/244435). – nhgrif

Antwort

2

Ich bin mir nicht sicher, ob Sie mit dem Versand arbeiten, aber NSTimer verhält sich in meinen Tests viel genauer. Hier ist ein Beispielcode eine Aktion numOfTimes mal

var iterationNumber = 0 
var startTime = CFAbsoluteTimeGetCurrent() 
let numOfTimes = 30 

// Start a repeating timer that calls 'action' every 0.3 seconds 
var timer = NSTimer.scheduledTimerWithTimeInterval(0.3, target: self, selector: #selector(ViewController.action), userInfo: nil, repeats: true) 

func action() { 
    // Print relevant information 
    let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime 
    print("\(Double(iterationNumber+1) * 0.3) - \(timeElapsed) : \(CFAbsoluteTimeGetCurrent())") 

    // Increment iteration number and check if we've hit the limit 
    iterationNumber += 1 
    if iterationNumber > numOfTimes { 
     timer.invalidate() 
    } 
} 

Ein Teil der Ausgabe des Programms auszuführen:

7.8 - 7.80285203456879 : 25 
8.1 - 8.10285001993179 : 26 
8.4 - 8.40283703804016 : 27 
8.7 - 8.70284104347229 : 28 
9.0 - 9.00275802612305 : 29 
9.3 - 9.3028250336647 : 30 

Es bleibt innerhalb weniger Millisekunden von der erwarteten Zeit (ich gehe davon aus, das ist die Zeit, es dauert CFAbsoluteTimeGetCurrent aufzurufen) und driftet nicht zusammen oder verklumpt sie nicht.

Ein Nachteil dieses Ansatzes besteht darin, dass es nicht jede Aktion wie im Code programmiert, obwohl ich mir nicht sicher bin, ob das für Sie wichtig ist.

+1

Die dokumentierte Präzision für 'NSTimer' ist 50-100 ms, siehe http://stackoverflow.com/questions/9737877/how-to -get-a-genau-timer-in-ios, so könnte dies gut genug sein. – Sulthan

+0

Gut zu wissen. Vielen Dank! – Bernem

+0

Außerdem bietet "NSTimer" einen Toleranzwert, mit dem Sie die Strenge festlegen können, mit der das System die Aufgabe pünktlich ausführen kann. Während der Standardwert 0 ist, legt "NSTimer" selbst dann, wenn die tatsächliche Genauigkeit ein wenig abweicht, automatisch das nächste Feuerdatum fest, unabhängig davon, wann das vorherige Feuer tatsächlich stattgefunden hat, um den Widerstand zu verhindern. –

1

Mit CADisplayLink, einer hohen Präzision Timer

(keine schöner Code, nur zusammen schnell gehackt)

var displayLink: CADisplayLink? 
var startTime: CFAbsoluteTime = 0 
var nextTime: CFAbsoluteTime = 0 
var index: Int = 0 

func testIncrement() { 
    self.startTime = CFAbsoluteTimeGetCurrent() 
    self.nextTime = self.startTime 

    displayLink = CADisplayLink(target: self, selector: #selector(execute)) 
    displayLink?.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes) 

    let expectation = expectationWithDescription("test") 

    self.waitForExpectationsWithTimeout(20.0, handler: nil) 
} 

func execute() { 
    let currentTime = CFAbsoluteTimeGetCurrent() 

    if (currentTime - nextTime < 0) { 
     return 
    } 

    let timeElapsed = currentTime - startTime 

    print("\(timeElapsed) : \(index)") 

    index += 1 
    nextTime = startTime + 0.3 * CFAbsoluteTime(index) 

    if (index > 30) { 
     displayLink?.removeFromRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes) 
    } 
} 

Beachten Sie, dass die Methode tatsächlich mehrmals ausgeführt wird und Sie intern prüfen müssen, ob genügend Zeit verstrichen ist.

Verwandte Themen