为什么 dispatchqueue.global() 在 deinit 之后还活着

why dispatchqueue.global() still alive after deinit

deinit {
    print("deinit")
}

DispatchQueue.global().async { [weak self] in
            
    while(true){
        print(self)
        print("sleep")
        sleep(1)
    }
}

虽然调用了class中的deinitDispatchQueue.global() 中的无限循环仍然存在。

在那种情况下,例如,

Optional(<OutOfKiosk.DialogFlowPopUpController: 0x150116e00>)

sleep

Optional(<OutOfKiosk.DialogFlowPopUpController: 0x150116e00>)

sleep

deinit (after deinit)

nil

sleep

nil

sleep

...(repeat)


如果有人教我为什么,我将非常感激

全局调度队列不在您应用的 class 中。它在系统中。您已经启动了一个独立的线程,它只是保持 运行 直到它的操作结束,在这种情况下永远不会。这与您的 class 是否获得 deinit 并且实例不复存在完全无关。

的确,一个常见的错误是启动一个独立的线程,一段时间后,引用您的实例。如果您的实例同时调用了 deinit,则可能会发生各种可怕的事情,从崩溃到在 deinit.

之后让您的实例处于不确定状态。

然而,这里并没有发生这种情况;你已经正确地使用了 weak self 所以你的实例确实已经井然有序地消失了,正如 nil 告诉你的那样。所以你看到的是预期的行为,尽管在现实生活中显然这不是一件好事。

DispatchQueue.global() returns 全局系统队列。 https://developer.apple.com/documentation/dispatch/dispatchqueue/2300077-global

GCD 管理一个共享线程池,决定并将代码块添加到全局调度队列中以执行它。

在Debug Memory Graph中,你可以找出许多活着的调度队列

您在调度队列上执行的执行与 DialogFlowPopUpController 实例的 deinit

无关
// your execution should not be completed because there are no break statement
{ [weak self] in
    while(true){
        print(self)
        print("sleep")
        sleep(1)
    }
}

如何更改您的执行以打破无限循环

DispatchQueue.global().async { [weak self] in
    while(self != nil){
        print(self)
        print("sleep")
        sleep(1)
    }
}

GCD 独立于对象的生命周期管理队列(以及分派到这些队列的任务)。

FWIW,您使用的是全局队列这一事实与手头的问题无关。使用自定义调度队列(或操作队列)会导致完全相同的行为:

let queue = DispatchQueue(label: "private_queue")
queue.async { [weak self] in
    while true {
        print(self)
        print("sleep")
        sleep(1)
    }
}

分派的任务,无论队列如何,使用 while 循环将继续执行,除非您明确退出循环(使用 self == nil 测试,或者,如果您想手动取消,使用isCancelled).

GCD 不执行抢先取消。


请注意,此调度任务当前 运行 甚至无关紧要。只关心分派的任务是否完成。 while 循环无关紧要。考虑:

let queue = DispatchQueue(label: "private_queue")
for i in 0 ..< 100 {
    queue.async { [weak self] in
        print("iteration \(i) queued by \(self)")
        Thread.sleep(forTimeInterval: 1)
    }
}
print("done dispatching")

这会将 100 个任务排入该自定义串行队列。但是如果 self 在所有这些任务完成之前被释放,这个队列将继续处理这 100 个已分派的任务。

归根结底,虽然我们可以讨论 GCD 对全局队列的管理,但这不是重点。主要观察结果是:

  • 添加到调度队列的任务(或添加到操作队列的操作)有效地保持对其各自队列的强引用;

  • 分派的工作项(或操作)将继续执行,而不管将其排队的对象可能已被释放(当然,除非您测试是否 self == nil) ;和

  • 即使一个派遣的任务(或操作)还没有开始,它会一直留在那个队列中直到它执行完(或者你手动取消它)...再一次,是否无关紧要排队它的对象是否已被释放。