• Swift - KVO初探

    2019-02-28 00:58:51
    swift 4.0 KVO使用 swift4.0 中使用了不同于OC的API,使用起来更加简单。但是也需要注意几点: 是 swift class 需要在声明的时候增加 @objcMembers 关键字 被观察的属性需要用dynamic修饰 不需要在对象被回收时手动...

    swift 4.0 KVO使用

    swift4.0 中使用了不同于OC的API,使用起来更加简单。但是也需要注意几点:

    • 是 swift class 需要在声明的时候增加 @objcMembers 关键字
    • 被观察的属性需要用dynamic修饰
    • 不需要在对象被回收时手动 remove observer,所以需要我们自己添加引用,否则当前函数离开后这个观察闭包就会被回收了。

    代码如下:

    class ViewController: UIViewController {
    
        var swiftClass: OCClass!
        var ob: NSKeyValueObservation!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            swiftClass = OCClass(name: "oc")
            ob = swiftClass.observe(\.name, changeHandler: { (ob, change) in
                let new = ob.name
                print(new)
            })
            swiftClass.willChangeValue(for: \.name)
            swiftClass.didChangeValue(for: \.name)
    //        swiftClass.name = "swift4"
            
        }
    }
    
    /// 注意添加 objcMembers
    @objcMembers class OCClass: NSObject {
        dynamic var name: String
        
        init(name: String) {
            self.name = name
        }
    }
    复制代码

    其实Swift和OC在KVO的底层原理都是一致的。

    iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?) 答. 当一个对象使用了KVO监听,iOS系统会修改这个对象的isa指针,改为指向一个全新的通过Runtime动态创建的子类,子类拥有自己的set方法实现,set方法实现内部会顺序调用willChangeValueForKey方法、原来的setter方法实现、didChangeValueForKey方法,而didChangeValueForKey方法内部又会调用监听器的observeValueForKeyPath:ofObject:change:context:监听方法。

    如何手动触发KVO 答. 被监听的属性的值被修改时,就会自动触发KVO。如果想要手动触发KVO,则需要我们自己调用willChangeValueForKey和didChangeValueForKey方法即可在不改变属性值的情况下手动触发KVO,并且这两个方法缺一不可。

    参考文档:

    Swift 4新知:KVC和KVO新姿势 iOS底层原理总结 - 探寻KVO本质

    展开全文
  • KVO and Swift

    2016-07-18 09:55:25
    不像Objective-c中的类,Swift类对于KVO并没有原生的支持,不过你可以在类型安全的前提下使用属性观察者轻松的完成相同的目标. 不管如何,从NSObject类派生出的类是支持KVO的,如果你想在不使用属性观察者的情况下使用...

    不像Objective-c中的类,Swift类对于KVO并没有原生的支持,不过你可以在类型安全的前提下使用属性观察者轻松的完成相同的目标.

    不管如何,从NSObject类派生出的类是支持KVO的,如果你想在不使用属性观察者的情况下使用KVO,你只要从NSObject类中继承你的类.

    不幸的是,即使从NSObject类派生,KVO也不是自动开启的,你希望用KVO观察的属性必须用dynamic关键字标记,才可以字Swift中的类中使用KVO.

    展开全文
  • KVC 与 KVO 使用姿势和原理解析
  • Swift中,原本没有KVO模式: KVO本质上是基于runtime的动态分发机制,通过key来监听value的值。 OC能够实现监听因为都遵守了NSKeyValueCoding协议 OC所有的类都是继承自NSObject,其默认已经遵守了该协议,但...

     Swift中,原本没有KVO模式:

        KVO本质上是基于runtime的动态分发机制,通过key来监听value的值。

        OC能够实现监听因为都遵守了NSKeyValueCoding协议

        OC所有的类都是继承自NSObject,其默认已经遵守了该协议,但Swift不是基于runtime的, Swift 中的属性处于性能等方面的考虑默认是关闭动态分发的,只有在属性前加 dynamic才会开启运行时,允许监听属性的变化。

        

        KVO在OC和Swift中的区别:

        

        OC中,所有的类继承自 NSObject ,它对 KVO提供了默认实现,但Swift不是。

        原因有二:

        第一:Swift 中继承自NSObject的属性处于性能等方面的考虑,默认是关闭动态分发的, 所以无法使用KVO,不过可以在属性前加上dynamic来打开。

        

        class Person:NSObject {

            //name不支持KVO监听,age支持KVO

            var name: String

            dynamic var age: Int = 0

            

            init(name: String,age: Int) {

                self.name = name

                self.age =age

            }

            第二: 不是所有的类都继承自NSObject,不继承自的对象

            

         例如:

            

            class Person {

                var name: String?

                var age: Int = 0

                

                init(name: String,age: Int) {

                    self.name = name

                    self.age =age

                }

                该类无法调用

                

                open func addObserver(_ observer: NSObject, forKeyPath keyPath: String, options: NSKeyValueObservingOptions = [], context: UnsafeMutableRawPointer?)

                方法,所以肯定无法使用KVO观察者模式,但Swift中提供了属性观察器(didSet,willSet)来解决这种问题;

                

                class Father: NSObject {

                    

                    var firstName: String = "First" {

                        willSet {   //新值设置之前被调用

                            print("willSet的新值是\(newValue)")

                        }

                        didSet { //新值设置之后立即调用

                            print("didSet的新值是\(oldValue)")

                        }

                    }

                }

                KVO的正常使用:(“三步走”思想)

                

                第一步:注册

                open func addObserver(_ observer: NSObject, forKeyPath keyPath: String, options: NSKeyValueObservingOptions = [], context: UnsafeMutableRawPointer?)

                

                第二步:监听

                override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)

                

                第三步:移除

                open func removeObserver(_ observer: NSObject, forKeyPath keyPath: String)

                代码演示

                

                

                import UIKit

                

                //监听UISlider的滑动,把滑动的结果传递给UIProgressView,以显示滑动进度

                

                class viewController: UIViewController {

                    @IBOutlet weak var slider: UISlider!

                    @IBOutlet weak var progressView: UIProgressView!

                    

                    override func viewDidLoad() {

                        super.viewDidLoad()

                        

                        // Do any additional setup after loading the view.

                        self.progressView.addObserver(self, forKeyPath: "progress", options: .new, context: nil)

                        self.slider.addObserver(self, forKeyPath: "value", options: .new, context: nil)

                    }

                    

                    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

                        

                        if keyPath == "value" {

                            if let value = change?[NSKeyValueChangeKey.newKeyasFloat {

                                self.progressView.progress = value/self.slider.maximumValue

                                view.alpha = CGFloat(self.progressView.progress)

                                self.textLabel.font = UIFont.systemFont(ofSize: view.alpha * 20)

                                self.textField.text = self.father?.firstName

                            }

                        }

                    }

                    

                    override func didReceiveMemoryWarning() {

                        super.didReceiveMemoryWarning()

                        // Dispose of any resources that can be recreated.

                    }

                    

                    deinit {

                        self.progressView.removeObserver(self, forKeyPath: "progress")

                        self.slider.removeObserver(self, forKeyPath: "value")

                    }

                }

    展开全文
  • Swift 实现KVO监听

    2019-06-10 13:07:29
    然而在Swift中是没有KVO模式的(换句话说是不能直接使用KVO模式),使用的条件必须是继承自NSObject,属性前加上dynamic来开启运行时,或者是在class前面加@objcMembers,所有的属性都会开启runtime,允许监听属性的...

    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框架,后续会爬一爬它的内部实现原理。

    最后

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

    展开全文
  • dynamic 被@objc dynamic 修饰的内容会具有动态性,比如调用方法会走runtime那一套流程 class Dog: NSObject { @objc dynamic func test1() {} func test2() {} } var d = Dog() ...Swift支持KVC...

    dynamic

    • 被@objc dynamic 修饰的内容会具有动态性,比如调用方法会走runtime那一套流程
    class Dog: NSObject {
        @objc dynamic func test1() {}
        func test2() {}
    }
    var d = Dog()
    d.test1()
    d.test2()
    
    • 对应汇编
      在这里插入图片描述
      在这里插入图片描述

    KVC\KVO

    • Swift支持KVC\KVO的条件
    • 属性所在的类、监听器最终继承自NSObject
    • 用@objc dynamic 修饰对应的属性
    class Observer: NSObject {
        override func observeValue(forKeyPath keyPath: String?,
                                   of object: Any?,
            change: [NSKeyValueChangeKey: Any]?,
            context: UnsafeMutableRawPointer?) {
            print("observeValue", change?[.newKey] as Any)
        }
    }
    
    class Person: NSObject {
        @objc dynamic var age: Int = 0
        var observer: Observer = Observer()
        override init() {
            super.init()
            self.addObserver(observer,
                             forKeyPath: "age",
                             options: .new,
                             context: nil)
        }
        deinit {
            self.removeObserver(observer,
                                forKeyPath: "age")
        }
    }
    var p = Person()
    // observeValue Optional(20)
    p.age = 20
    // observeValue Optional(25)
    p.setValue(25, forKey: "age")
    

    block方式的KVO

    class Person: NSObject {
        @objc dynamic var age: Int = 0
        var observation: NSKeyValueObservation?
        override init() {
            super.init()
            observation = observe(\Person.age, options: [.new, .old]) {
                (person, change) in
                print(change.newValue as Any)
            }
        }
    }
    var p = Person()
    // Optional(20)
    p.age = 20
    // Optional(25)
    p.setValue(25, forKey: "age")
    
    展开全文
  • SwiftKVO实例

    2019-02-27 09:56:07
    KVO(Key-Value-Observer)机制属于观察者模式。在对象属性变化时发送消息给观察者对象。   自定义观察者类: //NSObject类实现了NSKeyValueOberving协议,只需继承NSObject类即可 class MyObserver: 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...
  • 一句话使用KVO使用完无需自己移除KVO
  • 简单的kvo,描述的不太好,仅供初学者参考
  • ios swift4之kvo使用

    2019-04-28 00:45:44
    swift4之前,使用kvo只需要继承NSObject 就可以了。但是swift4之后发生了变化,必须在class之前加入@objcMembers修饰,不然就没有效果了。 下面举个例子:如定义一个Person类想监听其kvo的变化除了继承NSObject...
  • Swift4.2 KVO监听

    2019-03-20 10:41:31
    var observation: NSKeyValueObservation? override func viewDidLoad() { super.viewDidLoad() self.observation = self.downView.observe(\UIView.isHidden, options: [.new]) { (_, change) i...
  • KVO 在iOS应用场景还是挺多的, 虽然Swift新增属性观察器,但是在深层观察属性,KVO在一些场景还是比较方便。 环境 Swift Version:4.0 Xcode:10.1 (10B61) iphoneOS:12.1   问题 先看下代码,我...
  • 直接上代码import UIKitclass ViewController: UIViewController { let per = Person() override func viewDidLoad() { super.viewDidLoad() /// 添加观察者 per.addObserver(self, forKeyPath
  • Swift中KVC和KVO使用

    2014-10-29 20:04:44
    Swift使用KVC和KVO的类都必须必须继承自NSObject KVC key-value coding 是一种间接访问对象的机制 key的值就是属性名称的字符串,返回的value是任意类型,需要自己转化为需要的类型 KVC主要就是两个方法 (1)通过...
  • swift 中的KVO用法

    2017-03-14 16:35:52
    KVO 属性观察器
  • 2019独角兽企业重金招聘Python工程师标准>>> ...
  • Swift 下怎么使用 KVO

    2019-06-21 10:58:40
    但是 KVO 这个东西在 OC 这样一门动态语言下也是极其强大的,不容忽视,现在开始玩 Swift , 那怎么在 Swift 环境下使用 KVO 呢? 在 Swift 中我们也是可以使用 KVO 的,但是仅限于在 NSObject 的子类中。这是因为 KVO...
1 2 3 4 5 ... 20
收藏数 2,753
精华内容 1,101