2016-04-18 23 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) 

dopo il 30 esegue la sua quasi una seconda via, e la console di cominciare ad agire strano con che mostra due e due linee di stampa contemporaneamenteCome eseguire un'azione esattamente dopo 0,3 secondi per un dato numero di volte?

9.0 - 9.88806998729706 : 30 
9.3 - 9.88832598924637 : 31 

Esiste un modo per un XCTest per avvicinarsi al effettivamente facendo le richieste " al momento giusto "? Come ottenere la richiesta che dovrebbe essere fatto dopo 9 secondi non essere fatto dopo 9,88 secondi ..

+1

Ho rimosso il tag 'XCTest' in quanto non ha nulla di specifico da fare con XCTests. È solo una coincidenza che tu abbia bisogno di questo per un test. Anche se ... mi piacerebbe vedere cosa stai provando a testare in questo modo (e poi aggiungere il tag). – nhgrif

+0

@nhgrif è importante che XCTest sia mentione perché in realtà chiama waitForExpectationsWithTimeout() - Non sono sicuro che funzionerà con, ad esempio, il blocco del thread principale nei test – hakonbogen

+1

Se ritieni che menzionare 'XCTest' è importante, allora tu * davvero * bisogno di fornire il test che stai cercando di eseguire per il contesto. Così com'è ora, questo è un po 'un [problema XY] (http://meta.stackexchange.com/q/66377/244435). – nhgrif

risposta

2

Non sono sicuro se si sta utilizzando l'invio, ma NSTimer si sta comportando in modo molto più accurato nei miei test. Ecco alcuni esempi di codice per eseguire un azione numOfTimes volte

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

Alcuni degli output del programma:

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 

Rimane nel giro di pochi millisecondi del tempo previsto (sto assumendo che questo sia il momento serve per chiamare CFAbsoluteTimeGetCurrent) e non va alla deriva o raggruppa insieme.

Uno svantaggio di questo approccio è che non pianifica tutte le azioni in anticipo come nel codice, anche se non sono sicuro che ciò sia importante per te.

+1

La precisione documentata per 'NSTimer' è 50-100 ms, vedi http://stackoverflow.com/questions/9737877/how-to -get-a-accurate-timer-in-ios quindi questo potrebbe essere abbastanza buono. – Sulthan

+0

Buono a sapersi. Grazie! – Bernem

+0

Inoltre, 'NSTimer' fornisce un valore di tolleranza che consente di specificare la quantità di rigore che il sistema deve eseguire in tempo. Mentre il valore predefinito è 0, anche se la precisione effettiva è un po 'spenta, 'NSTimer' pianifica automaticamente la sua prossima data di fuoco a prescindere da quando il fuoco precedente si è effettivamente verificato per impedire il trascinamento. –

1

Utilizzando CADisplayLink, un timer ad alta precisione

(non un codice di bello, appena messo insieme veloce)

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

Nota: il metodo viene effettivamente eseguito più volte e internamente è necessario verificare se è trascorso un tempo sufficiente.

Problemi correlati