如何将后台任务标识符传递给完成处理程序?

How to pass background task identifier to completion handler?

我看到很多使用 class 成员变量将后台任务的值传递给完成块的示例:

self.bgTask = UIApplication.sharedApplication()
    .beginBackgroundTaskWithName("bg task", expirationHandler: { () -> Void in
        UIApplication.sharedApplication().endBackgroundTask(self.bgTask)
        self.bgTask = UIBackgroundTaskInvalid
        })

如果此代码被调用两次,第二次是在第一个后台任务仍然是 运行 时,那么 self.bgTask 将被新任务的标识符覆盖,并且 UIApplication.sharedApplication().endBackgroundTask(self.bgTask) 可能永远不会接到电话。

如果我声明一个局部变量,那么它的值在初始化之前被闭包捕获。也不行。

如何安全地将任务 ID 传递给它的完成处理程序?如果我上面的推理有误,请指正。

在 ObjC 中,您通常会使用 __block 变量来执行此操作,例如:

__block UIBackgroundTaskIdentifier bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        NSLog(@"Expired: %lu", (unsigned long)bgTask);
    }];
NSLog(@"Background: %lu", (unsigned long)bgTask);

这会捕获 bgTask 作为块的可变变量。当块被复制时,它被移动到堆中,局部变量 bgTask 成为指向它的间接指针。请参阅 Blocks Tips & Tricks.

中 bbum 更详尽的解释

如果您想将其分配给 属性,没问题,但您不需要。

Swift 没有 __block 属性,但它在没有任何帮助的情况下做了正确的事情。您只需要给它一个 var 即可使用。

func goBackground() {
    let app = UIApplication.sharedApplication()
    var bgTask: UIBackgroundTaskIdentifier = 0
    bgTask = app.beginBackgroundTaskWithExpirationHandler({
        NSLog("Expired: %lu", bgTask)
        app.endBackgroundTask(bgTask)
    })
    NSLog("Background: %lu", bgTask)
}

func applicationDidEnterBackground(application: UIApplication) {
    goBackground()
    goBackground()
}

同样,没有必要将值存储在 属性 中,除非您出于其他原因需要它。该值作为局部变量存储在闭包内。

这是了解闭包的重要内容,也是 Swift 的一个非常强大的功能。这与让您执行此操作相同:

var n = 0
let nextNat = { n++ }

println(nextNat()) // => 0
println(nextNat()) // => 1

您必须结束上一个任务并开始一个新任务 `

var uiBackgroundTaskIdentifier: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier(rawValue: 0)

func beingOrEndBackgroundUpdateTask() {
    DispatchQueue.main.async { [self] in
        switch UIApplication.shared.applicationState {
        case .background, .inactive:
            beginBackgroundUpdateTask()
        case .active:
            endBackgroundUpdateTask()
        default:
            break
        }
    }
}

func beginBackgroundUpdateTask() {
    // end previous task
    endBackgroundUpdateTask()
    self.uiBackgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask (withName: "Finish Network Tasks") {
        // End the task if time expires.
        self.endBackgroundUpdateTask()
    }
}

func endBackgroundUpdateTask() {
    UIApplication.shared.endBackgroundTask(self.uiBackgroundTaskIdentifier)
    self.uiBackgroundTaskIdentifier = UIBackgroundTaskIdentifier.invalid
}

`