如何向包含 dispatch_after() 的函数添加回调?

How do I add a callback to a func that contains a dispatch_after()?

我想在几秒钟后关闭 UIAlertView(不需要 'ok' 按钮)。

经过一些研究,我发现我可以使用 dispatch_after 作为最终解除警报的延迟:

func delay(delay:Double, closure:()->()) {  
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))  // ...Int64.init() struct in Swift.
        ),
        dispatch_get_main_queue(), closure)
}

来源:Using dispatch_after vs NSTimer

上面的代码工作正常,但我想编辑闭包以允许传递 sender:UIViewController 参数。这就是我感到困惑的地方。

dispatch_after() 格式为:

func dispatch_after(_ when: dispatch_time_t, _ queue: dispatch_queue_t, _ block: dispatch_block_t)

这是我尝试过的(错误):

func delay(delay:Double, closure:(sender:UIViewController)->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))  // ...Int64.init() struct in Swift.
        ),
        dispatch_get_main_queue(), closure:(sender:UIViewController) in {

        })
}

问题:如何解决这个问题,使 block/closure 参数包含回调引用 ('sender:UIViewController')?

你不需要"pass"sender来关闭。您只需要在闭包中引用 sender

这被称为 capture,它是闭包的有用之处之一。闭包将 capture 引用 sender 并带走它。就像魔术一样。

所以把闭包写在sender范围内的地方。在闭包内引用 sender。然后,将闭包传递给 delay,一切都会正常工作:

func closeAlert(sender: UIVewController) {

    let closure: () -> () = { 
        doSomethingWith(sender)
    }

    delay(3, closure: closure)
}

同样,秘诀在于闭包捕获对 sender 的引用,当闭包传递给 delay.

以下 (abridged) 代码显示我的警报对话框(没有按钮)几秒钟然后消失。

我正在考虑使用 NSTimer,但了解到这是不明智的。
使用 Swift + dispatch_after() 是真正的方法。

showAlert(sender: self, withTitle: "No Blisses", withMessage: "You don't have any Blisses to show.", alertPurpose: .timed)

enum AlertPurpose:Int {
    case none = 0  // ...default: no alert button.
    case simple // ...generic OK response.
    case timed
    case startBliss
    case noVideo
    case createdHashtag
    case missingProfileImage
    case profileSaved
    case settings
}

func closeAlert(sender: UIViewController) {
    let closure: () -> () = {
        sender.dismissViewControllerAnimated(true, completion: nil)
    }
    delay(3, closure)
}

func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}
...
...
...
} else if alertPurpose == .timed {
        closeAlert(sender as! UIViewController)