这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战
1. RxSwift.Resources 介绍
RxSwift.Resources 是 RxSwift 中用于记录引用的结构体
private let resourceCount = AtomicInt(0)
/// Resource utilization information
public struct Resources {
/// Counts internal Rx resource allocations (Observables, Observers, Disposables, etc.). This provides a simple way to detect leaks during development.
public static var total: Int32 {
load(resourceCount)
}
/// Increments `Resources.total` resource count.
///
/// - returns: New resource count
public static func incrementTotal() -> Int32 {
increment(resourceCount)
}
/// Decrements `Resources.total` resource count
///
/// - returns: New resource count
public static func decrementTotal() -> Int32 {
decrement(resourceCount)
}
}
2. RxSwift.Resources 实现简单理解
final class AtomicInt: NSLock {
fileprivate var value: Int32
public init(_ value: Int32 = 0) {
self.value = value
}
}
AtomicInt 类为 NSLock 子类,添加了value 属性用于记录引用计数
@discardableResult
@inline(__always)
func add(_ this: AtomicInt, _ value: Int32) -> Int32 {
this.lock()
let oldValue = this.value
this.value += value
this.unlock()
return oldValue
}
@discardableResult
@inline(__always)
func sub(_ this: AtomicInt, _ value: Int32) -> Int32 {
this.lock()
let oldValue = this.value
this.value -= value
this.unlock()
return oldValue
}
@discardableResult
@inline(__always)
func fetchOr(_ this: AtomicInt, _ mask: Int32) -> Int32 {
this.lock()
let oldValue = this.value
this.value |= mask
this.unlock()
return oldValue
}
添加 add、sub、fetchOr 方法用于操纵 AtomicInt 的 value
问题1:fetchOr 方法 是用来做什么的
@inline(__always)
func load(_ this: AtomicInt) -> Int32 {
this.lock()
let oldValue = this.value
this.unlock()
return oldValue
}
@discardableResult
@inline(__always)
func increment(_ this: AtomicInt) -> Int32 {
add(this, 1)
}
@discardableResult
@inline(__always)
func decrement(_ this: AtomicInt) -> Int32 {
sub(this, 1)
}
封装方法:
- 读取计数方法
load - 计数 +1 方法
increment - 计数 -1 方法
decrement
3. RxSwift.Resources 使用
import UIKit
open class ZTBaseViewController: UIViewController {
public lazy var ZT_RX_TOTAL: Int32 = 0
public lazy var disposeBag = DisposeBag()
open override func viewDidLoad() {
super.viewDidLoad()
self.ZT_RX_TOTAL = RxSwift.Resources.total
}
deinit {
let msg = self.description
let ZT_RX_TOTAL = self.ZT_RX_TOTAL
DispatchQueue.main.async {
if RxSwift.Resources.total != ZT_RX_TOTAL {
let alert = UIAlertController(title: "init (ZT_RX_TOTAL)\n deinit (RxSwift.Resources.total)", message: msg, preferredStyle: .alert)
alert.addAction(.init(title: "OK", style: .cancel, handler: nil))
UIViewController.getCurrentViewController()?.present(alert, animated: true, completion: nil)
}
}
}
}
通过判断 viewDidLoad() 和 deinit 后 引用计数是否相同,简单判断 ZTBaseViewController 及其子类 使用的 RxSwift 对象是否释放完
注意点:在 viewDidLoad() 方法前不能初始化 RxSwift 对象,如需必要使用 lazy 延迟创建 RxSwift 对象
扩展:这个思路可以用于其他类 如为 UIView 添加子类,在使用 RxSwift 对象之前记录引用计数,然后在 deinit 时对比计数是否一致
4. 常见引用计数不一致原因
-
在
ZTBaseViewController及其子类的生命周期内,为类似单例对象创建RxSwift对象- 解决方法:在使用单例对象前后计算出新增
RxSwift对象,ZTBaseViewController的ZT_RX_TOTAL+ 新增对象数
- 解决方法:在使用单例对象前后计算出新增
-
RxSwift对象之间存在循环引用 -
ZTBaseViewController内存泄露