kvo的使用 swift4.0_swift4.2 kvo - CSDN
  • Swift - KVO初探

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

    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本质

    展开全文
  • swift4.0中的KVO

    2019-07-10 02:04:50
    swift4.0KVO 出现了变更,添加了监听回调.变得更加简单易用.同时会自动释放.不用再remove了.但是相对于OC中使用来说还是有一些需要注意的地方.1. 需要在当前类中添加一个属性来持有NSKeyValueObservation 否则...
    在swift4.0 中 KVO 出现了变更,添加了监听回调.变得更加简单易用.同时会自动释放.不用再remove了.但是相对于OC中使用来说还是有一些需要注意的地方.
    1. 需要在当前类中添加一个属性来持有
    NSKeyValueObservation 否则在函数执行完毕后将会被释放
    2. 被监听的类需要用 @objcMembers 修饰,否则会报错
    3. 你需要监听哪个属性,则该属性需要 dynamic 修饰,否则不会触发回调

    举个简单的例子.一个用于被监听的模型

    @objcMembers class UserModel : NSObject {

        dynamic var name : String = ""

        var age : Int = 0

    }

     

     注意: 1.该UserModel被 @objcMembers 修饰.

          2.模型中的属性 name 被 dynamic 修饰, 可被监听. 而 age 则无法被监听,如果想要监听 age 则同样需要用 dynamic 进行修饰

     

     

    KVO的使用

     

    1.在一个类中(如ViewController)我们添加一个属性 

        var model : UserModel! = {

            var model = UserModel.init()

            model.name = "张三" 

         model.age = 20

            return model

        }()

     

    2. 在类中再添加一个属性 

     

    var ob : NSKeyValueObservation?

     

    来持有 监听 NSKeyValueObservation

     

    然后写一个方法并且在合适的地方调用

     

       func setupKVO() {

          // 注意这里用self.ob来持用该监听 model为需要监听的类实例.调用时需要确保其不为nil 

            self.ob = model!.observe(\UserModel.name) { (model, change) in

            print("model.name")

            }

        }

     

    这样.当 model.name 的值改变时就会触发回调

    转载于:https://www.cnblogs.com/luobenpaidui/p/9354410.html

    展开全文
  • KVO 在iOS应用场景还是挺多的, 虽然Swift新增属性观察器,但是在深层观察属性,KVO在一些场景还是比较方便。 环境 Swift Version:4.0 Xcode:10.1 (10B61) iphoneOS:12.1   问题 先看下代码,我...

    KVO 在iOS应用场景还是挺多的, 虽然Swift新增属性观察器,但是在深层观察属性,KVO在一些场景还是比较方便。

    环境


    Swift Version:4.0
    Xcode:10.1 (10B61)
    iphoneOS:12.1

     

    问题


    先看下代码,我要观察currentOrderNum这个属性的新值变化

    Model:

    
    class OrderModel: NSObject {
       var currentOrderNum: Int = 1
    
    }
    
    

    Controller新增观察者:

    
     currentModel.addObserver(self, forKeyPath: "currentOrderNum", options: NSKeyValueObservingOptions.new, context: nil)
     
    

    回调:

    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
            if keyPath == "currentOrderNum" {
                if let newNumberValue = change?[NSKeyValueChangeKey.newKey] as? Int{
                    calcultorTotal(number: newNumberValue)
                }
            }
        }
        
    

    问题出来了,这样写是不会回调的。

     

    分析和解决


    我们知道Swift用到动态派发的时候,依赖OC的运行时。
    KVO原理需要用到运行时,苹果在Swift4.0的时候,只有在标有@objc的时候才不静态派发,放到运行时的时候决定。如果没有标@objc默认属性静态派发,导致KVO监听该属性无法工作。

    解决(将该属性标记@objc 动态派发):

    Model:

    
    class OrderModel: NSObject {
          @objc dynamic var currentOrderNum: Int = 1
    }
    
    
    展开全文
  • key-path 通常是用在键值编码(KVC)与键值观察(KVO)上的,KVC、KVO 相关内容可以参考我之前写的这篇文章:Swift - 反射(Reflection)的介绍与使用样例(附KVC介绍) 1.Swift 3 之前使用的是 String 类型的 key-...

    一、Key Paths 新语法

    key-path 通常是用在键值编码(KVC)与键值观察(KVO)上的,KVC、KVO 相关内容可以参考我之前写的这篇文章:Swift - 反射(Reflection)的介绍与使用样例(附KVC介绍)

    1.Swift 3 之前使用的是 String 类型的 key-Path

    //用户类
    class User: NSObject{
        @objc var name:String = ""  //姓名
        @objc var age:Int = 0  //年龄
    }
     
    //创建一个User实例对象
    let user1 = User()
    user1.name = "hangge"
    user1.age = 100
     
    //使用KVC取值
    let name = user1.value(forKey: "name")
    print(name)
     
    //使用KVC赋值
    user1.setValue("hangge.com", forKey: "name")
    复制代码

    具体显示如下:

    2.到了 Swift 3 新增了 #keyPath() 写法 使用 #keyPath() 写法,可以避免我们因为拼写错误而引发问题。

    //用户类
    class User: NSObject{
        @objc var name:String = ""  //姓名
        @objc var age:Int = 0  //年龄
    }
     
    //创建一个User实例对象
    let user1 = User()
    user1.name = "hangge"
    user1.age = 100
     
    //使用KVC取值
    let name = user1.value(forKeyPath: #keyPath(User.name))
    print(name)
     
    //使用KVC赋值
    user1.setValue("hangge.com", forKeyPath: #keyPath(User.name))
    复制代码

    3.Swift 4 中直接用 \ 作为开头创建 KeyPath 新的方式不仅使用更加简单,而且有如下优点:

    • 类型可以定义为 class、struct
    • 定义类型时无需加上 @objc 等关键字
    • 性能更好
    • 类型安全和类型推断,例如:user1.value(forKeyPath: #keyPath(User.name)) 返回的类型是 Any,user1[keyPath: \User.name] 直接返回 String 类型
    • 可以在所有值类型上使用 (1)比如上面的样例在 Swift4 中可以这么写:
    //用户类
    class User: NSObject{
        var name:String = ""  //姓名
        var age:Int = 0  //年龄
    }
     
    //创建一个User实例对象
    let user1 = User()
    user1.name = "hangge"
    user1.age = 100
     
    //使用KVC取值
    let name = user1[keyPath: \User.name]
    print(name)
     
    //使用KVC赋值
    user1[keyPath: \User.name] = "hangge.com"
    复制代码

    (2)keyPath 定义在外面也是可以的:

    let keyPath = \User.name
     
    let name = user1[keyPath: keyPath]
    print(name)
     
    user1[keyPath: keyPath] = "hangge.com"
    复制代码

    (3)可以使用 appending 方法向已定义的 Key Path 基础上填加新的 Key Path。

    let keyPath1 = \User.phone
    let keyPath2 = keyPath1.appending(path: \.number)
    复制代码

    二、类与协议的组合类型

    在 Swift 4 中,可以把类(Class)和协议(Protocol)用 & 组合在一起作为一个类型使用。

    使用样例1:

    protocol MyProtocol { }
     
    class View { }
     
    class ViewSubclass: View, MyProtocol { }
     
    class MyClass {
        var delegate: (View & MyProtocol)?
    }
     
    let myClass = MyClass()
    myClass.delegate = ViewSubclass() //这个编译正常
    myClass.delegate = View() //这个编译报错:
    复制代码

    具体错误信息如下:

    使用样例2:

    protocol Shakeable {
        func shake()
    }
     
    extension UIButton: Shakeable {
        func shake() {
            /* ... */
        }
    }
     
    extension UISlider: Shakeable {
        func shake() {
            /* ... */
        }
    }
     
    func shakeEm(controls: [UIControl & Shakeable]) {
        for control in controls where control.isEnabled {
            control.shake()
        }
    }
    复制代码

    三、下标支持泛型

    1.下标的返回类型支持泛型 有时候我们会写一些数据容器,Swift 支持通过下标来读写容器中的数据。但是如果容器类中的数据类型定义为泛型,过去下标语法就只能返回 Any,在取出值后需要用 as? 来转换类型。现在 Swift 4 定义下标也可以使用泛型了。

    struct GenericDictionary<Key: Hashable, Value> {
        private var data: [Key: Value]
         
        init(data: [Key: Value]) {
            self.data = data
        }
         
        subscript<T>(key: Key) -> T? {
            return data[key] as? T
        }
    }
     
    //字典类型: [String: Any]
    let earthData = GenericDictionary(data: ["name": "Earth", "population": 7500000000, "moons": 1])
     
    //自动转换类型,不需要在写 "as? String"
    let name: String? = earthData["name"]
    print(name)
     
    //自动转换类型,不需要在写 "as? Int"
    let population: Int? = earthData["population"]
    print(population)
    复制代码

    2.下标类型同样支持泛型

    extension GenericDictionary {
        subscript<Keys: Sequence>(keys: Keys) -> [Value] where Keys.Iterator.Element == Key {
            var values: [Value] = []
            for key in keys {
                if let value = data[key] {
                    values.append(value)
                }
            }
            return values
        }
    }
     
    // Array下标
    let nameAndMoons = earthData[["moons", "name"]]        // [1, "Earth"]
    // Set下标
    let nameAndMoons2 = earthData[Set(["moons", "name"])]  // [1, "Earth"]
    复制代码

    四、Codable 序列化

    如果要将一个对象持久化,需要把这个对象序列化。过去的做法是实现 NSCoding 协议,但实现 NSCoding 协议的代码写起来很繁琐,尤其是当属性非常多的时候。 Swift 4 中引入了 Codable 协议,可以大大减轻了我们的工作量。我们只需要让需要序列化的对象符合 Codable 协议即可,不用再写任何其他的代码。

    struct Language: Codable {
        var name: String
        var version: Int
    }
    复制代码

    1.Encode 操作 我们可以直接把符合了 Codable 协议的对象 encode 成 JSON 或者 PropertyList。

    let swift = Language(name: "Swift", version: 4)
     
    //encoded对象
    let encodedData = try JSONEncoder().encode(swift)
     
    //从encoded对象获取String
    let jsonString = String(data: encodedData, encoding: .utf8)
    print(jsonString)
    复制代码

    2.Decode 操作

    let decodedData = try JSONDecoder().decode(Language.self, from: encodedData)
    print(decodedData.name, decodedData.version)
    复制代码


    五、Subtring

    Swift 4 中有一个很大的变化就是 String 可以当做 Collection 来用,并不是因为 String 实现了 Collection 协议,而是 String 本身增加了很多 Collection 协议中的方法,使得 String 在使用时看上去就是个 Collection。

    let str = "hangge.com"
     
    print(str.prefix(5)) // "hangg"
    print(str.suffix(5)) // "e.com"
     
    print(str.dropFirst()) // "angge.com"
    print(str.dropLast()) // "hangge.co"
    复制代码

    比如上面的样例,我们使用一些 Collection 协议的方法对字符串进行截取,只不过它们的返回结果不是 String 类型,而是 Swift 4 新增的 Substring 类型。

    1.为何要引入 Substring? 既然我们想要的到的就是字符串,那么直接返回 String 就好了,为什么还要多此一举返回 Substring。原因只有一个:性能。具体可以参考下图: 当我们用一些 Collection 的方式得到 String 里的一部分时,创建的都是 Substring。Substring 与原 String 是共享一个 Storage。这意味我们在操作这个部分的时候,是不需要频繁的去创建内存,从而使得 Swift 4 的 String 相关操作可以获取比较高的性能。 而当我们显式地将 Substring 转成 String 的时候,才会 Copy 一份 String 到新的内存空间来,这时新的 String 和之前的 String 就没有关系了。

    2.使用 Substring 的注意事项 由于 Substring 与原 String 是共享存储空间的,只要我们使用了 Substring,原 String 就会存在内存空间中。只有 Substring 被释放以后,整个 String 才会被释放。 而且 Substring 类型无法直接赋值给需要 String 类型的地方,我们必须用 String() 包一层。当然这时系统就会通过复制创建出一个新的字符串对象,之后原字符串就会被释放。

    3.使用样例 这里对 String 进行扩展,新增一个 subString 方法。直接可以根据起始位置(Int 类型)和需要的长度(Int 类型),来截取出子字符串。

    extension String {
        //根据开始位置和长度截取字符串
        func subString(start:Int, length:Int = -1) -> String {
            var len = length
            if len == -1 {
                len = self.count - start
            }
            let st = self.index(startIndex, offsetBy:start)
            let en = self.index(st, offsetBy:len)
            return String(self[st ..< en])
        }
    }
    复制代码

    使用样例:

    let str1 = "欢迎访问hangge.com"
    let str2 = str1.subString(start: 4, length: 6)
    print("原字符串:\(str1)")
    print("截取出的字符串:\(str2)")
    复制代码

    运行结果如下:

    注意:这个方法最后我们会将 Substring 显式地转成 String 再返回。


    六、废除 swap 方法

    (1)过去我们会使用 swap(::) 来将两个变量的值进行交换:

    var a = 1
    var b = 2
    swap(&a, &b)
    print(a, b)
    复制代码

    (2)后面 swap() 方法将会被废弃,建议使用 tuple(元组)特性来实现值交换,也只需要一句话就能实现:

    var a = 1
    var b = 2
    (b, a) = (a, b)
    print(a, b)
    复制代码

    使用 tuple 方式的好处是,多个变量值也可以一起进行交换:

    var a = 1
    var b = 2
    var c = 3
    (a, b, c) = (b, c, a)
    print(a, b, c)
    复制代码

    (3)补充一下:现在数组增加了个 swapAt 方法可以实现两个元素的位置交换。

    var fruits = ["apple", "pear", "grape", "banana"]
    //交换元素位置(第2个和第3个元素位置进行交换)
    fruits.swapAt(1, 2)
    print(fruits)
    复制代码


    七、减少隐式 @objc 自动推断

    1.过去的情况(Swift 3) (1)在项目中如果想把 Swift 写的 API 暴露给 Objective-C 调用,需要增加 @objc。在 Swift 3 中,编译器会在很多地方为我们隐式的加上 @objc。 (2)比如当一个类继承于 NSObject,那么这个类的所有方法都会被隐式的加上 @objc。

    class MyClass: NSObject {
        func print() { } // 包含隐式的 @objc
        func show() { } // 包含隐式的 @objc
    }
    复制代码

    (3)但这样做很多并不需要暴露给 Objective-C 也被加上了 @objc。而大量 @objc 会导致二进制文件大小的增加。

    2.现在的情况(Swift 4) (1)在 Swift 4 中隐式 @objc 自动推断只会发生在下面这种必须要使用 @objc 的情况:

    • 覆盖父类的 Objective-C 方法
    • 符合一个 Objective-C 的协议

    (2)大多数地方必须手工显示地加上 @objc。

    class MyClass: NSObject {
        @objc func print() { } //显示的加上 @objc
        @objc func show() { } //显示的加上 @objc
    }
    复制代码

    (3)如果在类前加上 @objcMembers,那么它、它的子类、扩展里的方法都会隐式的加上 @objc。

    @objcMembers
    class MyClass: NSObject {
        func print() { } //包含隐式的 @objc
        func show() { } //包含隐式的 @objc
    }
     
    extension MyClass {
        func baz() { } //包含隐式的 @objc
    }
    复制代码

    (4)如果在扩展(extension)前加上 @objc,那么该扩展里的方法都会隐式的加上 @objc。

    class SwiftClass { }
     
    @objc extension SwiftClass {
        func foo() { } //包含隐式的 @objc
        func bar() { } //包含隐式的 @objc
    }
    复制代码

    (5)如果在扩展(extension)前加上 @nonobjc,那么该扩展里的方法都不会隐式的加上 @objc。

    @objcMembers
    class MyClass : NSObject {
        func wibble() { } //包含隐式的 @objc
    }
     
    @nonobjc extension MyClass {
        func wobble() { } //不会包含隐式的 @objc
    }
    复制代码

    ? 联系

    展开全文
  • key-path 通常是用在键值编码(KVC)与键值观察(KVO)上的,KVC、KVO 相关内容可以参考我之前写的这篇文章:Swift - 反射(Reflection)的介绍与使用样例(附KVC介绍)
  • //// Person.swift// 011-加载百度//// Created by 庄壮勇 on 2018/1/9.// Copyright © 2018年 Personal. All rights reserved.//import UIKit1.创建Personl 类class Person: NSObject { var name: String? ...
  • 扩展就是向一个已有的类、结构体或枚举类型添加新功能(functionality)。这包括在没有权限获取原始源代码的情况下扩展类型的能力(即逆向建模)。扩展和 Objective-C 中的分类(categories)类似。...定义和使用
  • 原来用swift 4.0编译NSTableView binding NSArrayController的时候,一运行就报: this class is not key value coding-compliant for the key dataArray. 然后我改成swift 3.2编译就OK了。
  • 1.首先创建一个Person的类 继承 ...// Person.swift // 008-构造函数(KVC) // // Created by 庄壮勇 on 2018/1/9. // Copyright © 2018年 Personal. All rights reserved. // import UIKit /**  1. 定义属性的时
  • IOS 视频播放器 swift

    2019-08-10 14:44:13
    IOS 视频播放器 swift
  • GitHub第三方资源库整理(Swift篇) Jisen 关注2016.05.30 18:04* 字数 2226 阅读 8734评论 4喜欢 103之前做了OC的整理,这一篇是Swift的第三方资源库整理。同样的,有好的项目我就会加进来,并把更新说明写在文末...
  • 目录(?)[+] 版本:Swift github排名 ...1. 将Swift从Objective-C混合的md文件中抽出(正在进行…) ...使用方法:根据目录关键字搜索,记得包含@,以保证搜索目录关键字的唯一性。 问题反馈:请广大网
  • Swift中的存储属性就是以前学习OC中的普通属性 在结构体或者类中定义的属性, 默认就是存储属性 struct Person { var name: String var age: Int } var p:Person = Person(name: "cdh", age: 20) p.name = "CDH" p....
  • RxSwift学习之六(调度者)
  • 原文地址:https://github.com/easyui/blog/blob/master/Swift/2017-09-26-Swift-Tips%5BV4.0%2B%2CXcode9.0%2B%5D.md   :smile:Xcode 9 中同时集成了 Swift 3.2 和 Swift 4。 Swift 3.2 完全兼容 Swift 3.1,...
  • IOS学习和总结KVO

    2015-08-28 16:51:18
    关于KVO,估计很多同学对KVO一样是很模糊的,都听到大家在说,面试会问,但是在开发中很少使用到,一般使用到的也是某个组件中封装使用,而这个封装又不是交由自己来完成,因此还是很模糊。现在想来,似乎我也不清楚...
  • MKPinAnnotationView和MKAnnotationView区别,MKAnnotationView可以使用静态图片作为实例作为view,如果想使用Apple自带的标准大头针,那么需要使用MKPinAnnotationView。 MKAnnotation Protocol Reference @...
  • Swift 知识小集

    2019-03-31 05:10:02
    以下内容均是笔者学习过程中收集的知识点,顺序比较跳跃,初衷是为了方便查阅,顺便加深记忆。内容会不断更新,如果有什么问题或者有好的 Swift ...Swift 4.0 Xcode 9.1 Associated Object Objective-C 的 runtime...
  • 大概是这样, Swift4.0出了, 重新梳理Swift知识, 对比了下RXSwift和ReactiveSwift, 喜欢ReactiveSwift多一些, 想了想, 出份基础教程. 建议新人朋友只看如何使用, 至于实现概述看看最后的总结和图了解一下思路就行了. ...
  • swift学习代码笔记

    2016-06-21 08:31:22
    swift 最基本的学习结束了,但是很多细节问题都没有搞的太明白,看以后的使用情况吧,在应用中再深入理解。明天就可以用swift开发项目了,高兴! //: Playground - noun: a place where people can play import ...
1 2 3 4 5 ... 20
收藏数 401
精华内容 160
关键字:

kvo的使用 swift4.0