• 一句话使用KVO,使用完无需自己移除KVO
  • iOS安全的KVO操作,解决由于添加了KVO忘记移除,或者多次移除导致的崩溃问题
  • Swift 实现KVO监听 2019-06-10 13:07:29
    Swift是没有KVO模式的 在iOS开发中,运用oc的runtime的动态分发机制,通过key来监听value的值,达到实现KVO监听的效果。然而在Swift中是没有KVO模式的(换句话说是不能直接使用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框架,后续会爬一爬它的内部实现原理。

    最后

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

    展开全文
  • Swift中,原本没有KVO模式: KVO本质上是基于runtime的动态分发机制,通过key来监听value的值。 OC能够实现监听因为都遵守了NSKeyValueCoding协议 OC所有的类都是继承自NSObject,其默认已经遵守了该协议,但...
  • Swift 4 前后 KVO 的变化 2019-03-04 14:04:14
    如果了解过设计模式的同学,应该都知道有一种设计...我们来研究一下 iOS 里对观察者模式的支持,即 KVO(key-value observing) ,键值对观察,其原理是基于 KVC(key-value-coding) 和 runtime。通过 Swfit 研究。...
  • Swift4 - KVO的浅析 2018-06-27 22:35:28
    KVO提供了一种机制能够方便的观察对象的属性。如:指定一个被观察对象,当对象的某个属性发生变化时,对象会获得通知,进而可以做出相应的处理。KVO实现原理官方文档具体描述如下:Automatic key-value observing is...
  • 来源:简书 - changsanjiang链接:http://www.jianshu.com/p/11bb1dcfb07e(点击尾部阅读原文前往)问题我们都知道, 使用KVO模式, 对某个属性进行监听时, Observer 需要在必要的时刻进行移除, 否则 App 必然会 ...
  • 2019独角兽企业重金招聘Python工程师标准>>> ...
  • 关于kvokvo能做什么?kvo作为cocoa框架的重要特性之一,在底层框架中被大量使用。在特定的场合使用该特性往往能够带来难以想象的好处,让整个方案变得相当简洁和优雅。比如大名鼎鼎的下拉刷新的svpulltorefresh...
  • 移除观察者身份 可以发送一条指定观察方对象和键
  • Swift 下怎么使用 KVO 2019-06-21 10:58:40
    但是 KVO 这个东西在 OC 这样一门动态语言下也是极其强大的,不容忽视,现在开始玩 Swift , 那怎么在 Swift 环境下使用 KVO 呢? 在 Swift 中我们也是可以使用 KVO 的,但是仅限于在 NSObject 的子类中。这是因为 KVO...
  • KVO编码(swift 2019-07-23 16:24:22
    KVO,即:Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受收到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就...使用kvo时,注意在界面销毁,移除监听,把指针置...
  • 窥探Swift编程之错误处理与异常抛出
  • https://blog.csdn.net/jq2530469200/article/details/52484646最近项目中处理kvo 的时候,遇到一个问题:当我操作的时候,会发现kvo 释放的时候,会崩溃, 崩溃日志如下:/*Terminating app due to uncaught ...
  • RxSwift KVO的实现原理 2019-12-10 22:47:22
    RxSwift中的KVO使用是不需要自己移除观察者的,那它的底层实现原理是什么呢? 案例使用 person.rx.observe(String.self, "name").subscribe(onNext: { (change) in print("observe订阅到了KVO:\(String...
  • WKWebView的使用,利用KVO 2017-03-07 22:14:06
    // // TGWebVC.h // // Created by targetcloud on 2017/3/7. // Copyright © 2017年 targetcloud. All rights reserved. // #import @interface TGWebVC : UIViewController @property (nonatomic, strong) NS
  • 前几篇KVO的文章介绍了一些KVO的使用方式和KVO的实现原理和自定义KVO,但是有几个问题没有解决,例如:如果监听多个属性,上篇文章中的自定义KVO就不能解决了,自动销毁等,本篇文章需要在上篇文章的基础上进行阅读...
1 2 3 4 5 ... 20
收藏数 828
精华内容 331