为什么这个dispatch_after立即开火?

时间:2015-07-25 17:18:36

标签: ios xcode swift grand-central-dispatch swift-playground

这是我编写的用于将NSTimeInterval转换为基于walltime的dispatch_time_t的结构:

public struct WallTimeKeeper {

    public static func walltimeFrom(spec: timespec)->dispatch_time_t {
        var mutableSpec = spec
        let wallTime = dispatch_walltime(&mutableSpec, 0)
        return wallTime
    }

    public static func timeStructFrom(interval: NSTimeInterval)->timespec {
        let nowWholeSecsFloor = floor(interval)
        let nowNanosOnly = interval - nowWholeSecsFloor
        let nowNanosFloor = floor(nowNanosOnly * Double(NSEC_PER_SEC))
        println("walltimekeeper: DEBUG: nowNanosFloor:        \(nowNanosFloor)")
        var thisStruct = timespec(tv_sec: Int(nowWholeSecsFloor),
            tv_nsec: Int(nowNanosFloor))
        return thisStruct
    }
}

我一直试图在游乐场测试它的准确性,但我的结果令我感到困惑。

这是我的Playground中的代码(在Sources文件夹中使用我的WallTimeKeeper):

var stop = false
var callbackInterval: NSTimeInterval?
var intendedTime: NSDate?
var intendedAction: ()->() = {}

func testDispatchingIn(thisManySeconds: NSTimeInterval){
    intendedTime = NSDate(timeIntervalSinceNow: thisManySeconds)
    intendedAction = stopAndGetDate
    dispatchActionAtDate()
    loopUntilAfterIntendedTime()
    let success = trueIfActionFiredPunctually() //always returns false
}

func dispatchActionAtDate(){
    let timeToAct = dateAsDispatch(intendedTime!)
    let now = dateAsDispatch(NSDate())
    /*****************
    NOTE: if you run this code in a Playground, comparing the above two
    values will show that WallTimeKeeper is returning times the
    correct number of seconds apart.
    ******************/
    dispatch_after(timeToAct, dispatch_get_main_queue(), intendedAction)
}

func loopUntilAfterIntendedTime() {
    let afterIntendedTime = intendedTime!.dateByAddingTimeInterval(1)
    while stop == false && intendedTime?.timeIntervalSinceNow > 0 {
        NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode,
            beforeDate: afterIntendedTime)
    }
}

func trueIfActionFiredPunctually()->Bool{
    let intendedInterval = intendedTime?.timeIntervalSinceReferenceDate
    let difference = intendedInterval! - callbackInterval!
    let trueIfHappenedWithinOneSecondOfIntendedTime = abs(difference) < 1
    return trueIfHappenedWithinOneSecondOfIntendedTime
}

func dateAsDispatch(date: NSDate)->dispatch_time_t{
    let intendedAsInterval = date.timeIntervalSinceReferenceDate
    let intendedAsStruct = WallTimeKeeper.timeStructFrom(intendedAsInterval)
    let intendedAsDispatch = WallTimeKeeper.walltimeFrom(intendedAsStruct)
    return intendedAsDispatch
}

func stopAndGetDate() {
    callbackInterval = NSDate().timeIntervalSinceReferenceDate
    stop = true
}

testDispatchingIn(3)

...所以trueIfActionFiredPunctually()不仅总是返回false,而且difference值 - 用于衡量回调触发时间和假定时间之间的差异< / em> to fire - 在一个成功的结果中应该真的接近0,当然低于1 - 而不是几乎完全相同的回调应该等待的时间

总结:定义了等待的时间量,并设置了在该时间量后触发的操作。当动作触发时,它会创建一个触发时刻的时间戳。当时间戳与它应该的值进行比较时,我们接近我们应该等待的时间,而不是接近于零。

换句话说,似乎传递给dispatch_after的动作立即被触发,绝对不应该这样做!

Playgrounds有问题或我的代码有问题吗?

修改 这是代码。在实时应用程序中运行相同的代码会产生相同的结果。我做错了什么?

1 个答案:

答案 0 :(得分:1)

我明白了。这是一个头脑风暴。如果有人遇到同样的问题,我会留下它。

我正在使用NSDate().timeIntervalSinceReferenceDate设置我的时间。

Walltimes需要NSDate().timeIntervalSince1970

dispatch_after任务全部立即被解雇,因为他们认为这些任务是在四十多年前安排的!

将所有内容更改为NSDate().timeIntervalSince1970会使其完美运行。

道德:除非你确定你的参考日期是1970年,否则不要使用walltimes!