未使用的不可变接收器的编译器警告,但使用 _ 导致测试失败

Compiler warning for unused immutable sink, but using _ causes test to fail

我有一个测试可以验证在新设备可用时是否正在发布消息:

let deviceConnectedPublisher = NotificationCenter.default.publisher(for: .deviceAdded)
            .compactMap { [=10=].object as AnyObject as? ConnectableDevice }

let sink = deviceConnectedPublisher.sink { _ in
    expectation.fulfill()
}

这工作得很好,但我有一个编译器警告:

Initialization of immutable value 'sink' was never used; consider replacing with assignment to '_' or removing it

但是如果我接受编译器的建议并将其更改为

let _ = deviceConnectedPublisher.sink { _ in
    expectation.fulfill()
}

然后我的测试超时,随后失败。有没有更好的方法来申报我的水槽?有什么方法可以告诉编译器这是错误的吗?这应该作为错误提交吗?我理解为什么编译器认为该变量未被使用,但在这种情况下似乎应该有一种方法来禁用警告——或者更有可能——我正在做一些不符合预期的事情。

编译器向您提示的是接收器需要存储。通常的做法是写

deviceConnectedPublisher.sink { _ in
    expectation.fulfill()
}.store(in: &self.storage)

...其中 storageSet<AnyCancellable>[AnyCancellable] 实例 属性。这使管道保持活动状态并赋予其自动绑定到周围对象(例如视图控制器)的生命周期。

在您将 AnyCancellable 存储在名为 sink 的本地 属性 中的代码中,编译器会发出您从未使用过该变量的警告。这意味着允许编译器优化变量并立即销毁 AnyCancellable

但是,您可能 运行 在调试构建配置中进行测试。调试配置的默认设置禁用优化。因此,编译器 不会 优化掉 sink 变量,并且只会在作用域结束时销毁它。到那时,我想您已经发布了您期望的通知。

在您使用 _ 的代码中,编译器会立即销毁返回的 AnyCancellable。这会取消您对通知发布者的订阅,因此通知永远不会发送到您的 sink 闭包。

您可以使警告静音,并确保本地 属性 的值直到稍后才被破坏,使用 withExtendedLifetime 函数:

let sink = deviceConnectedPublisher.sink { _ in
    expectation.fulfill()
}

// post notification here

withExtendedLifetime(sink) { }
// Only now can sink be destroyed.