Swift 实现KVO监听

Swift是没有KVO模式的

在iOS开发中,运用oc的runtime的动态分发机制,通过key来监听value的值,达到实现KVO监听的效果。然而在Swift中是没有KVO模式的(换句话说是不能直接使用KVO模式),使用的条件必须是继承自NSObject,属性前加上dynamic来开启运行时,或者是在class前面加@objcMembers,所有的属性都会开启runtime,允许监听属性的变化。

class Car: NSObject {

    dynamic var carName: String
    
    init(carName: String) {
        self.carName = carName;
    }
    
    
}

// ************或者加@objcMembers

@objcMembers
class Car: NSObject {

    var carName: String
    
    init(carName: String) {
        self.carName = carName;
    }
    
    
}
复制代码

然后你就可以愉快的像使用oc一样来玩耍KVO了,具体实现过程就不演示了,毕竟你们都是老司机了。

KVO的实现原理

基于oc的runtime的动态分发机制,属性对象被监听时,会在运行时创建一个派生类,并重写了被观察属性keyPath的setter方法,setter方法随后负责通知观察对象属性的改变状况。

OC中的KVO及其KVO的基础知识可参见: 深入runtime探究KVO

KVO的实现原理与具体应用

Swift中属性观察器

Swift有两个属性观察者willSet和didSet,类似于触发器。用来监视属性的除初始化之外的属性值变化,当属性值发生改变时可以对此作出响应。

有如下特点:

  • 不仅可以在属性值改变后触发didSet,也可以在属性值改变前触发willSet
  • 给属性添加观察者必须要声明清楚属性类型,否则编译器报错
  • willSet可以带一个newName的参数,没有的话,该参数默认命名为newValue
  • didSet可以带一个oldName的参数,表示旧的属性,不带的话默认命名为oldValue
  • 属性初始化时,willSet和didSet不会调用。只有在初始化上下文之外,当设置属性值时才会调用
  • 即使是设置的值和原来值相同,willSet和didSet也会被调用
 var firstName: String = "Fist" {
       willSet {   //新值设置之前被调用,在此可以进行条件筛选,过滤数据
           print("willSet的新值是\(newValue)")
       }
       didSet { //新值设置之后立即调用,在此可以进行条件筛选,过滤数据,可以直接绑定数据到UI上面
           print("didSet的旧值是\(oldValue) --- 新值是 \(firstName)")
           
           self.nameLabel.text = firstName
       }
   }
复制代码

用得更多的是在didSet中跟UI绑定,或者条件逻辑过滤。

Swift实现KVO值监听

利用设计模式中的观察者模式,观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。 观察者模式适用在一个被观察者(数据源)要通知多个观察者的场景。

Swift中实现KVO,还是用到了属性观察者didSet,直接贴代码:

public class Observable<Type> {
    
    // MARK: - Callback
    fileprivate class Callback {
        fileprivate weak var observer: AnyObject?
        fileprivate let options: [ObservableOptions]
        fileprivate let closure: (Type, ObservableOptions) -> Void
        
        fileprivate init(
            observer: AnyObject,
            options: [ObservableOptions],
            closure: @escaping (Type, ObservableOptions) -> Void) {
            
            self.observer = observer
            self.options = options
            self.closure = closure
        }
    }
    
    // MARK: - Properties  利用Swift 的didSet 特性把值回调给callback
    public var value: Type {
        didSet {
            removeNilObserverCallbacks()
            notifyCallbacks(value: oldValue, option: .old)
            notifyCallbacks(value: value, option: .new)
        }
    }
    
    private func removeNilObserverCallbacks() {
        callbacks = callbacks.filter { $0.observer != nil }
    }
    
    // MARK: 回调给callback 实现闭包回调
    private func notifyCallbacks(value: Type, option: ObservableOptions) {
        let callbacksToNotify = callbacks.filter { $0.options.contains(option) }
        callbacksToNotify.forEach { $0.closure(value, option) }
    }
    
    // MARK: - Object Lifecycle
    public init(_ value: Type) {
        self.value = value
    }
    
    // MARK: - Managing Observers
    private var callbacks: [Callback] = []
    
    
    /// 添加观察者
    ///
    /// - Parameters:
    ///   - observer: 观察者
    ///   - removeIfExists: 如果观察者存在需要移除
    ///   - options: 被观察者
    ///   - closure: 回调
    public func addObserver(
        _ observer: AnyObject,
        removeIfExists: Bool = true,
        options: [ObservableOptions] = [.new],
        closure: @escaping (Type, ObservableOptions) -> Void) {
        
        if removeIfExists {
            removeObserver(observer)
        }
        
        let callback = Callback(observer: observer, options: options, closure: closure)
        callbacks.append(callback)
        
        if options.contains(.initial) {
            closure(value, .initial)
        }
    }
    
    public func removeObserver(_ observer: AnyObject) {
        callbacks = callbacks.filter { $0.observer !== observer }
    }
}

// MARK: - ObservableOptions
public struct ObservableOptions: OptionSet {
    
    public static let initial = ObservableOptions(rawValue: 1 << 0)
    public static let old = ObservableOptions(rawValue: 1 << 1)
    public static let new = ObservableOptions(rawValue: 1 << 2)
    
    public var rawValue: Int
    
    public init(rawValue: Int) {
        self.rawValue = rawValue
    }
}

复制代码

以上代码出自 设计模式(Swift) - 3.观察者模式、建造者模式 这一篇文章中,欲知详情请移步查看。

使用列子:

class ViewController: UIViewController {
    // 用来管理观察者
    public class Observer {}
    
    var observer: Observer? // 当observer置为nil的时候,可观察对象会自动释放.
    let user = User(name: "yihai", info: ["name" : "Lewis"],person: Person(name: "lewis", age: 18))
   
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
       
        
        observer = Observer()
        user.name.addObserver(observer!, options: [.new]) { name, change in
            print("name:\(name), change:\(change)")
        }
        user.info.addObserver(observer!, options: [.new]) { info, change in
            print("info:\(info), change:\(change)")
        }
        
         user.person.addObserver(observer!, options: [.new]) { person, change in
            print("person:\(person), change:\(change)")
        }

    
    }


       override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        user.name.value = "Amel"
        user.info.value = ["info":"lewis info"]
        user.person.value = Person(name: "swift", age: 19)
        user.person.value.name = "我们不一样"
        user.info.value["car"] = "宝马"
        user.info.value.removeAll()
    }


}

public class User {
    // 被观察的属性需要是Observable类型
    public let name: Observable<String>
    public var info: Observable<Dictionary<String, Any>>
    public var person: Observable<Person>
    
    public init(name: String,info: Dictionary<String, Any>,person: Person) {
        self.name = Observable(name)
        self.info = Observable(info)
        self.person = Observable(person)
    }
}

public struct Person {
    var name: String
    var age: Int
    
    public init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}



}
复制代码
注意
  • 可以监听任意类型的对象
  • Dictionary的增删改支持,struct的改变某个内部属性值也可以
  • 有可能当observer置不为nil的时候,可观察对象收不到监听回调

那么对于数组Array的增删改是否一样也能实现呢?这个问题留给大神们亲自求索,我太菜了,哈哈。

第三方框架实现值监听

更方便,更简洁,更优雅

        /*  // RxSwift 实现方式  需要导入如下几个框架
            import RxSwift
            import RxCocoa
            import RxGesture
            import NSObject_Rx
        */
        

        let variable = Variable(Int())
        let variableDisposeBag = DisposeBag()

        variable.asObservable().subscribe { (number) in
                print("\(String(describing: number.element))")
            }.disposed(by: variableDisposeBag)

        variable.value = 100

复制代码

本人很喜欢RxSwift框架,后续会爬一爬它的内部实现原理。

最后

写得不是很好,请担待并提出您的宝贵意见。 希望我的文章能成为你的盛宴,也渴望你的建议能成为我的大餐。 如有错误请留言指正,对文章感兴趣可以关注作者不定期更新。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值