提问人:MrKew 提问时间:8/5/2023 最后编辑:MrKew 更新时间:8/5/2023 访问量:47
Observable.create 捕获行为
Observable.create capture behaviour
问:
为什么这会导致在执行此操作时尝试读取已解除分配的引用?printOnSelf("onFire")
调用后,应该有两个对 的引用:第一个来自 as,第二个来自捕获局部变量并因此包含对它的引用的闭包。subject!.startEmitter()
TimerInvalidator
Controller
let invalidator = TimerInvalidator()
Observable.create
invalidator
当被释放时,对 的第一个引用应该丢失,而第二个引用仍然在闭包中捕获。但是,由于 已解除分配,因此 也应该同时解除分配并处理创建的可观察序列。这应该解除对闭包的分配,从而删除对 .这应该意味着也应该被释放并使计时器无效。Controller
TimerInvalidator
Observable.create
Controller
DisposeBag
Observable.create
TimerInvalidator
TimerInvalidator
但实际上,计时器在释放后很久才会触发,从而导致访问时出错。我错过了什么?Controller
self
当然,不在 内部访问,而是在 create 方法之后的闭包内访问会更有意义。或者,当序列被处理时,可以通过更改来修复它:self
Observable.create
.do(onNext:)
return Disposables.create {
print("Emitter.start.create.dispose")
invalidator.timer?.invalidate()
}
或者可以通过更改以下内容来捕获它:TimerInvalidator
unowned
return Observable.create { [unowned self, unowned invalidator] event in
...
但我对原因很感兴趣,为什么这种方法不好。以及为什么这些修复程序有效。
下面的代码显然只是一个最小的例子,我作为一个包运行。
main.swift
import Foundation
import RxSwift
import RxCocoa
class TimerInvalidator {
var timer: Timer?
deinit {
print("TimerInvalidator.deinit")
timer?.invalidate()
}
}
class Controller {
let db = DisposeBag()
let invalidator = TimerInvalidator()
let emitter = Emitter()
func startEmitter() {
print("Controller.startEmitter")
emitter.createSignal(with: invalidator)
.emit() // Do stuff
.disposed(by: db)
}
deinit {
print("Controller.deinit")
}
}
class Emitter {
func printOnSelf(_ string: String) {
// Represents some operation on self
print("\(Self.self): " + string)
}
func createSignal(with invalidator: TimerInvalidator) -> Signal<String> {
return Observable.create { [unowned self] event in
printOnSelf("onCreated")
invalidator.timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { [unowned self] timer in
printOnSelf("onFire")
event.onNext("fire")
event.onCompleted()
}
return Disposables.create {
print("Emitter.start.create.dispose")
}
}
.asSignal(onErrorJustReturn: "error")
}
deinit {
print("Emitter.deinit")
}
}
var subject: Controller? = Controller()
subject!.startEmitter()
DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
subject = nil
}
RunLoop.current.run(mode: .default, before: Date() + 10)
包.swift(与问题没有直接关系,只是为了您的方便)
import PackageDescription
let package = Package(
name: "StackOverflow",
products: [
.executable(name: "StackOverflow", targets: ["StackOverflow"])
],
dependencies: [
.package(url: "https://github.com/ReactiveX/RxSwift", exact: "6.5.0")
],
targets: [
.target(
name: "StackOverflow",
dependencies: ["RxSwift", .product(name: "RxCocoa", package: "RxSwift")]),
]
)
答:
可观察的合约保证在调用处置袋时调用订阅。它不能保证与 Observable 关联的内存何时被清理。这取决于底层 iOS VM,如果你查看应用崩溃时的内存图,它仍然保留 Observable。dispose()
deinit
TimerInvalidator
您可能注意到,在应用程序崩溃时,您的 deinit 尚未被调用。但是,如果您删除有问题的行并让应用程序继续,则确实会调用 deinit。TimerInvalidator
请记住,无论底层内存模型是什么,Rx 系统都设计为具有可确定的行为。这就是为什么该函数要求您返回正确清理资源的原因。如果不这样做,你就违反了合同,使库的行为没有定义。create
Disposable
你说:
...它可以通过在处理序列时使计时器失效来修复......
事实上,这是解决它的唯一有效方法(如遵循合同)。
评论