RxSwift.Resources

608 阅读2分钟

这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战

1. RxSwift.Resources 介绍

RxSwift.ResourcesRxSwift 中用于记录引用的结构体

      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
}

添加 addsubfetchOr 方法用于操纵 AtomicIntvalue

问题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 对象,ZTBaseViewControllerZT_RX_TOTAL + 新增对象数
  • RxSwift 对象之间存在循环引用

  • ZTBaseViewController 内存泄露