How to perform an action exactly after 0.3 seconds for a given number of times?

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)

after 30 executes its almost a second off, and console start acting weird with showing two and two print lines simultaneously

9.0 - 9.88806998729706 : 30
9.3 - 9.88832598924637 : 31

Is there any way for an XCTest to get closer to actually doing the requests "on correct time"? Like getting the request that should be done after 9 seconds not being done after 9.88 seconds..


Not sure if you're set on using dispatch, but NSTimer is behaving much more accurately in my testing. Here's some sample code to run an action numOfTimes times

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

Some of the output of the program:

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

It stays within a few milliseconds of the expected time (I'm assuming that's the time it takes to call CFAbsoluteTimeGetCurrent) and doesn't drift or clump them together.

One disadvantage to this approach is that it doesn't schedule each action all up front like it does in your code, although I'm not sure if that matters to you.


Using CADisplayLink , a high precision timer

(not a nice code, just hacked together fast)

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

Note the method is actually executed multiple times and internally you have to check whether enough time elapsed.

链接地址: http://www.djcxy.com/p/33516.html

上一篇: 检测Flask应用程序是否处理URL

下一篇: 如何在0.3秒后精确地执行一个动作达到给定的次数?