一、RxSwift内存管理
1.observeWeakly
接下来我们来看一个例子:
self.person.rx.observe(String.self, "name")
.subscribe(onNext: { change in
print("observe订阅到了KVO:\(String(describing: change))")
}).disposed(by: disposeBag)
self.person.rx.observeWeakly(String.self, "name")
.subscribe(onNext: { change in
print("observeWeakly订阅到了KVO:\(String(describing: change))")
}).disposed(by: disposeBag)
看到了两个不同的observe observeWeakly,分别订阅了name的change。
observe和observeWeakly 有什么区别吗?
1.observe有可能会出现释放不干净的情况
2.observeWeakly连weak一起监听解决释放不干净的情况
进入源码查看:
再次进入observeWeaklyKeyPathFor:
private func observeWeaklyKeyPathFor(_ target: NSObject, keyPath: String, options: KeyValueObservingOptions) -> Observable<AnyObject?> {
let components = keyPath.components(separatedBy: ".").filter { $0 != "self" }
let observable = observeWeaklyKeyPathFor(target, keyPathSections: components, options: options)
.finishWithNilWhenDealloc(target)
if !options.isDisjoint(with: .initial) {
return observable
}
else {
return observable
.skip(1)
}
}
切割字符components 在传入observeWeaklyKeyPathFor 处理返回序列。断点调试看看:
获取到propertyName属性名称,propertyAttributes 属性的详细pointervalue。在通过isWeakProperty 判断是否是弱引用声明。在注册KVOObservable 属性监听。 在propertyObservable.flatMapLatest接收KVOObservable属性变化监听的回调的容错处理。 这里注意先执行.subscribe订阅闭包 在执行.flatMapLatest闭包。
进入KVOObservable代码:
KVOObservable
继承ObservableType实现KVOObservableProtocol协议
,保存属性,循环订阅属性消息on(.next)
2.RxSwift-KVO流程
现在我们看KVOObserver
类,KVOObservable->subscribe->KVOObserver
(parent: self)做了什么。
private final class KVOObserver
: _RXKVOObserver
, Disposable {
typealias Callback = (Any?) -> Void
var retainSelf: KVOObserver?
init(parent: KVOObservableProtocol, callback: @escaping Callback) {
#if TRACE_RESOURCES
_ = Resources.incrementTotal()
#endif
super.init(target: parent.target, retainTarget: parent.retainTarget, keyPath: parent.keyPath, options: parent.options.nsOptions, callback: callback)
self.retainSelf = self
}
override func dispose() {
super.dispose()
self.retainSelf = nil
}
deinit {
#if TRACE_RESOURCES
_ = Resources.decrementTotal()
#endif
}
}
_RXKVOObserver->继承NSObject为什么呢?
第一个想到的是获取对象的属性参数,得到元类信息。
第二个这个可能是个中间类,对rx的属性path进行观察(观察者移交)。
在看super.init->_RXKVOObserver.init
进入断点调试
从断点上看"student.nickName"订阅了两次,一层一层的订阅,在_RXKVOObserver进行KVO属性监听。
然后发现observeWeaklyKeyPathFor
是一个递归调用的方法。
so:回到最初的observeWeakly
就是底层实现了KVO对属性的监听。
3.Rx内存管理
然后我们在回到KVOObservable
代码看到:
在_RXKVOObserver
在内部也是弱引用接收target
unowned
var 就是oc中的 unsafe_unretained
一样效果。(为什么用unsafe_unretained而不是weak呢)因为性能更高。
现在我们看一个例子:
myClosure = { [weak self] in
self?.name = "NY"
}
self.myClosure!()
deinit {
print("\(self)销毁 VC")
}
上面代码如果不加[weak self]
会产生循环引用,这个是基本情况。那如果出现别的情况呢?
也可以把weak换成unowned也能解决循环引用。
myClosure = { [weak self] in
DispatchQueue.global().asyncAfter(deadline: .now()+2) {
self?.name = "NY"
print(self?.name as Any)
}
}
修改代码增加异步延迟操作2秒,在赋值打印运行程序。
我们发现并没有崩溃,也没有报错。只是打印nil,但是这个结果并不友好,不利于我们发现问题。
这么解决nil问题呢,这个代码大家都会的。再次声明self 为强引用
guard let self = self else { return } //4.2版本之后 保留关键字
self.name = "NY"
print(self.name as Any)
接下来在看一个例子:
self.accountTF.rx.text.orEmpty
.subscribe(onNext:{ text in
self.title = text
}).disposed(by: disposeBag)
会发生循环引用吗? 没有输出销毁VC,这段代码发生了循环引用!!!具体是为什么呢?
修改代码添加 [weak self]
self.accountTF.rx.text.orEmpty
.subscribe(onNext:{ [weak self]text in
self?.title = text
}).disposed(by: disposeBag)
看到运行结果,销毁了VC并且rx计数归0。
核心问题:
那么RX的引用计数是在什么时候销毁归0的呢?
我们在DisposeBase
中设置断点,运行运行代码调试.
在首页显示,VC销毁之后rx在进行引用计数减法。
继续修改代码:
self.accountTF.rx.text.orEmpty
.bind(to: self.rx.title)
.disposed(by: disposeBag)
这样修改后的代码会发生循环引用吗? 看打印就已经知道,这样修改后没有发生循环引用。为什么呢?rxswift内部是这么处理的呢?
在通过一个例子研究一下:
self.observable = Observable<Any>.create { anyObserver -> Disposable in
anyObserver.onNext("Hello World")
print(self)
return Disposables.create()
}
self.observable?.subscribe(onNext: {
print("订阅到了:\($0) --")
}).disposed(by: self.disposeBag)
在创建序列时打印print(self),这里会产生循环引用吗? 答案是会产生循环引用,并没有销毁VC。 self->observable->create{}->self
这步要注意.subscribe中调用print(self)也会循环引用。
继续看代码:
Observable<Any>.create { anyObserver -> Disposable in
self.observer = anyObserver
anyObserver.onNext("Hello World")
return Disposables.create()
}
.subscribe(onNext: { item in
print(self)
print("订阅到了:\(item)")
})
.disposed(by: self.disposeBag)
这个代码会产生循环引用吗?
答案是会,self -> anyObserver -> subscribe -> self
所以产生了循环引用关系。
let vc = LGDetialViewController()
vc.publicOB.subscribe(onNext:{ item in
print("在\(self)订阅到了\(item)")
}).disposed(by: disposeBag)
这段代码中会产生rxswift计数循环引用,并不是VC页面上的循环引用。 每次点击都会增加rx的引用计数。为什么呢?
我们看到打印VC并没有销毁,是vc产生了循环引用。怎么处理这个问题呢?怎么销毁掉VC?
当前DetialViewController的disposed用的不应该是用VC的disposeBag。
vc.disposeBag
然后在运行代码
看到了rx计数并没有累加,问解决。
或者使用_ = vc.publicOB.take(until: vc.rx.deallocated)
也能解决问题,任务直到vc.rx.deallocated 详情页销毁的时候结束。
如果当DetialViewController页是用mySubject.onCompleted()结束,vc中使用DetialViewController的let dvc声明需要在DetialViewController页中再次激活:
mySubject = PublishSubject<Any>()//激活
总结
observeWeakly
:的主要递归函数是observeWeaklyKeyPathFor
对属性进行监听,KVOObservable
是最终接收监听属性的类。RxSwift-KVO流程
:KVOObservable->subscribe->KVOObserver
->_RXKVOObserver(移交观察者)-OC原生KVORx内存管理
:unowned
var 就是oc中的unsafe_unretained
一样效果。及在RxSwift中[weak self]的使用,RxSwift自身有针对内部使用的引用计数,主要是用disposeBag来进行维护。subscribe(onNext)和subscribe{}的执行代码不一样,结果也不一样subscribe(onNext)容易出现循环引用(要注意细节)。