2019-06-17 14:03:51 ZY_FlyWay 阅读数 193
  • iOS从初级到精通就业 Objective-C

    C语言基础入门,面向初级,毫无编程经验的学生群体,通过学习能够掌握C语言的基本语法,编程的基本概念,培养自己的编程思维!OC语言详解,掌握OC核心内容(面向对象、内存管理,类的扩展,KVC和KVO,闭包和常见的Foundation类库的使用方法等,为后续学习iOS SDK打好基础。

    10123 人正在学习 去看看 栾斌

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
}

2018-07-23 14:15:00 weixin_30949361 阅读数 4
  • iOS从初级到精通就业 Objective-C

    C语言基础入门,面向初级,毫无编程经验的学生群体,通过学习能够掌握C语言的基本语法,编程的基本概念,培养自己的编程思维!OC语言详解,掌握OC核心内容(面向对象、内存管理,类的扩展,KVC和KVO,闭包和常见的Foundation类库的使用方法等,为后续学习iOS SDK打好基础。

    10123 人正在学习 去看看 栾斌
在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

2019-03-20 10:41:31 YY_Seven 阅读数 80
  • iOS从初级到精通就业 Objective-C

    C语言基础入门,面向初级,毫无编程经验的学生群体,通过学习能够掌握C语言的基本语法,编程的基本概念,培养自己的编程思维!OC语言详解,掌握OC核心内容(面向对象、内存管理,类的扩展,KVC和KVO,闭包和常见的Foundation类库的使用方法等,为后续学习iOS SDK打好基础。

    10123 人正在学习 去看看 栾斌

var observation: NSKeyValueObservation?

override func viewDidLoad() {

        super.viewDidLoad()

        self.observation = self.downView.observe(\UIView.isHidden, options: [.new]) { (_, change) in

            if let hidden = change.newValue {

               self.collectionTableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: hidden ? 0 : self.downView.frame.height, right: 0)

            }

        }

}

2015-09-14 11:39:23 maomaoyu3211 阅读数 16
  • iOS从初级到精通就业 Objective-C

    C语言基础入门,面向初级,毫无编程经验的学生群体,通过学习能够掌握C语言的基本语法,编程的基本概念,培养自己的编程思维!OC语言详解,掌握OC核心内容(面向对象、内存管理,类的扩展,KVC和KVO,闭包和常见的Foundation类库的使用方法等,为后续学习iOS SDK打好基础。

    10123 人正在学习 去看看 栾斌

KVO (Key-Value Observing) 是 Cocoa 中公认的最强大的特性之一,但是同时它也以烂到家的 API 和极其难用著称。和属性观察不同,KVO 的目的并不是为当前类的属性提供一个钩子方法,而是为了其他不同实例对当前的某个属性 (严格来说是 keypath) 进行监听时使用的。其他实例可以充当一个订阅者的角色,当被监听的属性发生变化时,订阅者将得到通知。

 

在 Swift 中我们也是可以使用 KVO 的,观察者和被观察者都必须是 NSObject 的子类。这是可以理解的,因为 KVO 是基于 KVC (Key-Value Coding) 以及动态派发技术实现的,而这些东西都是 Objective-C 运行时的概念,这也意味着 Swift 中强大的 Struct,Enum以及泛型都与 KVO 无缘了。另外由于 Swift 为了效率,默认禁用了动态派发,因此想用 Swift 来实现 KVO,我们还需要做额外的工作,那就是将想要观测的对象标记为 dynamic,表示该属性的存取都由 runtime 在运行时来决定。除此之外,在 NSObject 子类中几乎没有属性默认是使用@dynamic 修饰(该关键字最常见场景是在 Core Data 里, NSManagedObject 子类的属性都是 dynamic 的),所以若想对某个属性进行观察,还必须在当前的子类中 override 该属性,override 时,采用 super 的实现即可。

所以Swift要使用KVO必须满足:

  1. 观察者和被观察者都必须是 NSObject 的子类
  2. 监听属性必须标记dynamic

 

原理:

  • 当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。
  • 派生类在被重写的 setter 方法实现真正的通知机制,键值观察通知依赖于 NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey: 。在一个被观察属性发生改变之前, willChangeValueForKey: 一定会被调用,这就 会记录旧的值。而当改变发生后, didChangeValueForKey: 会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。可以手动实现这些调用,但很少有人这么做。这么做是基于设置属性会调用 setter 方法,而通过重写就获得了 KVO 需要的通知机制。当然前提是要通过遵循 KVO 的属性设置方式来变更属性值,如果仅是直接修改属性对应的成员变量,是无法实现 KVO 的。
  • 同时派生类还重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。
  • 系统将这个对象的 isa 指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对 setter 的调用就会调用重写的 setter,从而激活键值通知机制。
  • 派生类还重写了 dealloc 方法来释放资源。

看下面代码:

-----------------start------------

对象变量名字: people

对象: <TestKVOKVC.People: 0x7fa449c14a60>

类: TestKVOKVC.People

元类: TestKVOKVC.People

实现的方法: setAge:, setSex:, age, sex, initWithName:age:sex:address:, name, address, .cxx_destruct, init, setName:, setAddress:, 

-----------------end------------

-----------------start------------

对象变量名字: namePeople

对象: <TestKVOKVC.People: 0x7fa449c61e20>

类: TestKVOKVC.People

元类: NSKVONotifying_TestKVOKVC.People

实现的方法: setAge:, setName:, class, dealloc, _isKVOA, 

-----------------end------------

-----------------start------------

对象变量名字: nameAgePeople

对象: <TestKVOKVC.People: 0x7fa449cbe990>

类: TestKVOKVC.People

元类: NSKVONotifying_TestKVOKVC.People

实现的方法: setAge:, setName:, class, dealloc, _isKVOA, 

-----------------end------------

0x00000001083a3c60

0x00000001089807fa

0x00000001083a3c60

0x00000001089807fa

可以看到输出类名始终为:TestKVOKVC.People,这是因为新诞生的派生类重写了 -class 方法声称它就是起初的基类,只有使用 runtime 函数 object_getClass 才能一睹芳容:NSKVONotifying_TestKVOKVC.People。注意看:name,age 两个被观察对象真正的类型都是 NSKVONotifying_TestKVOKVC.People,而且该类实现了:setAge:, setName:, class, dealloc, _isKVOA 这些方法。其中 setAge:, setName:重写实现通知机制, class声明原来的类 , dealloc释放资源,私有方法 _isKVOA 是用来标示该类是一个 KVO 机制声称的类。在这里 swift 做了一些优化,它对所有被观察对象只生成一个派生类,该派生类实现所有被观察对象的 setter 方法,这样就减少了派生类的数量,提供了效率。所有 NSKVONotifying_TestKVOKVC.People 这个派生类重写了 setAge,setName方法(留意:没重写其他set方法)。

接着来看最后两行输出,地址 0x00000001083a3c60 是 TestKVOKVC.People 类中的实现,而地址是 0x00000001089807fa 是派生类 NSKVONotifying_TestKVOKVC.People 类中的实现。那后面那个地址到底是什么呢?可以通过 lldb 的 info 命令 image lookup --address  0x00000001089807fa 查看该地址的信息:

(lldb) image lookup --address  0x00000001089807fa

      Address: Foundation[0x00000000000637fa] (Foundation.__TEXT.__text + 401690)

      Summary: Foundation`_NSSetObjectValueAndNotify

 

看起来它是 Foundation 框架提供的私有函数:_NSSetObjectValueAndNotify。更进一步,我们来看看 Foundation 到底提供了哪些用于 KVO 的辅助函数。打开 terminal,使用 nm -a 命令查看 Foundation 中的信息:

nm -a /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation

其中查找到我们关注的函数:

00000000000c76bf t __NSSetBoolValueAndNotify

0000000000152ab2 t __NSSetBoolValueForKeyInIvar

00000000000c74c8 t __NSSetBoolValueForKeyWithMethod

00000000000997db t __NSSetCharValueAndNotify

0000000000064759 t __NSSetCharValueForKeyInIvar

00000000000646f1 t __NSSetCharValueForKeyWithMethod

000000000001bd73 t __NSSetCheckSize

00000000003f7a50 b __NSSetClass

00000000001019af t __NSSetDirectory2

00000000000ab4d3 t __NSSetDoubleValueAndNotify

0000000000152b5d t __NSSetDoubleValueForKeyInIvar

00000000000abf65 t __NSSetDoubleValueForKeyWithMethod

0000000000139763 T __NSSetExceptionRaiser

0000000000180650 t __NSSetFileModificationUNIXTime

00000000000c9cca t __NSSetFloatValueAndNotify

0000000000152bb6 t __NSSetFloatValueForKeyInIvar

0000000000152712 t __NSSetFloatValueForKeyWithMethod

00000000000c9ba9 t __NSSetIntValueAndNotify

0000000000152c0f t __NSSetIntValueForKeyInIvar

00000000000abefb t __NSSetIntValueForKeyWithMethod

00000000001817a7 T __NSSetLogCStringFunction

00000000000a5389 t __NSSetLongLongValueAndNotify

0000000000152d5f t __NSSetLongLongValueForKeyInIvar

00000000000a55e1 t __NSSetLongLongValueForKeyWithMethod

000000000015abd7 t __NSSetLongValueAndNotify

0000000000152cb7 t __NSSetLongValueForKeyInIvar

0000000000152779 t __NSSetLongValueForKeyWithMethod

000000000011651f T __NSSetMainBundle

00000000001531ad t __NSSetNilValueForKey

00000000000647b0 t __NSSetObjectSetIvarValueForKeyInIvar

0000000000096e38 t __NSSetObjectValueAndNotify

0000000000028b2d t __NSSetObjectValueForKeyInIvar

00000000000c0657 t __NSSetPointValueAndNotify

0000000000152e5f t __NSSetPointValueForKeyInIvar

00000000000c05f1 t __NSSetPointValueForKeyWithMethod

000000000015b067 t __NSSetRangeValueAndNotify

0000000000152ebc t __NSSetRangeValueForKeyInIvar

00000000001528b2 t __NSSetRangeValueForKeyWithMethod

0000000000099610 t __NSSetRectValueAndNotify

0000000000152f15 t __NSSetRectValueForKeyInIvar

000000000015291d t __NSSetRectValueForKeyWithMethod

000000000015ae1f t __NSSetShortValueAndNotify

0000000000152db3 t __NSSetShortValueForKeyInIvar

0000000000152849 t __NSSetShortValueForKeyWithMethod

00000000000af692 t __NSSetSizeValueAndNotify

0000000000152f86 t __NSSetSizeValueForKeyInIvar

00000000000af62d t __NSSetSizeValueForKeyWithMethod

000000000015aab3 t __NSSetUnsignedCharValueAndNotify

0000000000152b09 t __NSSetUnsignedCharValueForKeyInIvar

00000000001526a9 t __NSSetUnsignedCharValueForKeyWithMethod

00000000000e59a2 t __NSSetUnsignedIntValueAndNotify

0000000000152c63 t __NSSetUnsignedIntValueForKeyInIvar

0000000000103f5e t __NSSetUnsignedIntValueForKeyWithMethod

0000000000096fa8 t __NSSetUnsignedLongLongValueAndNotify

00000000000f5f93 t __NSSetUnsignedLongLongValueForKeyInIvar

00000000000c745d t __NSSetUnsignedLongLongValueForKeyWithMethod

000000000015acfa t __NSSetUnsignedLongValueAndNotify

0000000000152d0b t __NSSetUnsignedLongValueForKeyInIvar

00000000001527e1 t __NSSetUnsignedLongValueForKeyWithMethod

000000000015af43 t __NSSetUnsignedShortValueAndNotify

0000000000152e09 t __NSSetUnsignedShortValueForKeyInIvar

0000000000103ef5 t __NSSetUnsignedShortValueForKeyWithMethod

 

Foundation 提供了大部分基础数据类型的辅助函数,此外还包括一些常见的 Cocoa 结构体如 Point, Range, Rect, Size,这表明这些结构体也可以用于自动键值观察,但要注意除此之外的结构体就不能用于自动键值观察了。

//
//  People.swift
//  TestKVOKVC
//
//  Created by yangjun zhu on 15/9/12.
//  Copyright © 2015年 Cactus. All rights reserved.
//

import Foundation


class People: NSObject{
    dynamic var name: String
    dynamic var age: Int
    dynamic var sex: Int
    var address: Address?
    
    init(name: String, age: Int, sex: Int, address: Address){
        self.name = name
        self.age = age
        self.sex = sex
        self.address = address
        
    }
}

 

 

//
//  PrincipleViewController.swift
//  TestKVOKVC
//
//  Created by yangjun zhu on 15/9/12.
//  Copyright © 2015年 Cactus. All rights reserved.
//

import UIKit

class PrincipleViewController: UIViewController {
    let people = People(name: "Owen", age: 1, sex: 1, address: Address())
    let namePeople = People(name: "Owen", age: 1, sex: 1, address: Address())
    let nameAgePeople = People(name: "Owen", age: 1, sex: 1, address: Address())
    
    deinit{
        self.namePeople .removeObserver(self, forKeyPath: "name")
        self.nameAgePeople .removeObserver(self, forKeyPath: "name")
        self.nameAgePeople .removeObserver(self, forKeyPath: "age")
        
        
    }
    
    private var myContext = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        self.namePeople.addObserver( self, forKeyPath: "name", options: NSKeyValueObservingOptions([.New, .Old]), context: &myContext)
        self.nameAgePeople.addObserver( self, forKeyPath: "name", options: NSKeyValueObservingOptions([.New, .Old]), context: &myContext)
        self.nameAgePeople.addObserver( self, forKeyPath: "age", options: NSKeyValueObservingOptions([.New, .Old]), context: &myContext)
        self.printDescription("people", obj: people)
        self.printDescription("namePeople",obj: namePeople)
        self.printDescription("nameAgePeople",obj: nameAgePeople)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    
    
    
    
    private func classMethodNames(c: AnyObject) -> [String]
    {
        var arr = [String]()
        var methodCount: CUnsignedInt = 0;
        let methodList = class_copyMethodList(object_getClass(c), &methodCount);
        
        for i in 0...methodCount {
            arr.append( NSStringFromSelector(method_getName(methodList[Int(i)])))
        }
        
        free(methodList);
        
        return arr;
    }
    
    private func printDescription(objectName: String, obj: AnyObject)
    {
        print("-----------------start------------")
        print("对象变量名字:",objectName)
        print("对象:",obj)
        print("类:",NSStringFromClass(obj.classForCoder))
        print("元类:",NSStringFromClass(object_getClass(obj)))
        print("实现的方法:",self.classMethodNames(obj).joinWithSeparator(", "))
        print("-----------------end------------")
        
    }
    
    /*
    func observeValueForKeyPath(keyPath: String?,  object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
    
    if let change = change where context == &myContext{
    print(keyPath, "改变了")
    print(keyPath, "new:" , change[NSKeyValueChangeNewKey])
    print(keyPath, "old:" , change[NSKeyValueChangeOldKey])
    return;
    }
    super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
    }
    */
    
}

 

输出:

-----------------start------------

对象变量名字: people

对象: <TestKVOKVC.People: 0x7fa449c14a60>

类: TestKVOKVC.People

元类: TestKVOKVC.People

实现的方法: setAge:, setSex:, age, sex, initWithName:age:sex:address:, name, address, .cxx_destruct, init, setName:, setAddress:, 

-----------------end------------

-----------------start------------

对象变量名字: namePeople

对象: <TestKVOKVC.People: 0x7fa449c61e20>

类: TestKVOKVC.People

元类: NSKVONotifying_TestKVOKVC.People

实现的方法: setAge:, setName:, class, dealloc, _isKVOA, 

-----------------end------------

-----------------start------------

对象变量名字: nameAgePeople

对象: <TestKVOKVC.People: 0x7fa449cbe990>

类: TestKVOKVC.People

元类: NSKVONotifying_TestKVOKVC.People

实现的方法: setAge:, setName:, class, dealloc, _isKVOA, 

-----------------end------------

0x00000001083a3c60

0x00000001089807fa

0x00000001083a3c60

0x00000001089807fa

可以看到输出类名始终为:TestKVOKVC.People,这是因为新诞生的派生类重写了 -class 方法声称它就是起初的基类,只有使用 runtime 函数 object_getClass 才能一睹芳容:NSKVONotifying_TestKVOKVC.People。注意看:name,age 两个被观察对象真正的类型都是 NSKVONotifying_TestKVOKVC.People,而且该类实现了:setAge:, setName:, class, dealloc, _isKVOA 这些方法。其中 setAge:, setName:重写实现通知机制, class声明原来的类 , dealloc释放资源,私有方法 _isKVOA 是用来标示该类是一个 KVO 机制声称的类。在这里 swift 做了一些优化,它对所有被观察对象只生成一个派生类,该派生类实现所有被观察对象的 setter 方法,这样就减少了派生类的数量,提供了效率。所有 NSKVONotifying_TestKVOKVC.People 这个派生类重写了 setAge,setName方法(留意:没重写其他set方法)。

接着来看最后两行输出,地址 0x00000001083a3c60 是 TestKVOKVC.People 类中的实现,而地址是 0x00000001089807fa 是派生类 NSKVONotifying_TestKVOKVC.People 类中的实现。那后面那个地址到底是什么呢?可以通过 lldb 的 info 命令 image lookup --address  0x00000001089807fa 查看该地址的信息:

(lldb) image lookup --address  0x00000001089807fa

      Address: Foundation[0x00000000000637fa] (Foundation.__TEXT.__text + 401690)

      Summary: Foundation`_NSSetObjectValueAndNotify

 

看起来它是 Foundation 框架提供的私有函数:_NSSetObjectValueAndNotify。更进一步,我们来看看 Foundation 到底提供了哪些用于 KVO 的辅助函数。打开 terminal,使用 nm -a 命令查看 Foundation 中的信息:

nm -a /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation

其中查找到我们关注的函数:

00000000000c76bf t __NSSetBoolValueAndNotify

0000000000152ab2 t __NSSetBoolValueForKeyInIvar

00000000000c74c8 t __NSSetBoolValueForKeyWithMethod

00000000000997db t __NSSetCharValueAndNotify

0000000000064759 t __NSSetCharValueForKeyInIvar

00000000000646f1 t __NSSetCharValueForKeyWithMethod

000000000001bd73 t __NSSetCheckSize

00000000003f7a50 b __NSSetClass

00000000001019af t __NSSetDirectory2

00000000000ab4d3 t __NSSetDoubleValueAndNotify

0000000000152b5d t __NSSetDoubleValueForKeyInIvar

00000000000abf65 t __NSSetDoubleValueForKeyWithMethod

0000000000139763 T __NSSetExceptionRaiser

0000000000180650 t __NSSetFileModificationUNIXTime

00000000000c9cca t __NSSetFloatValueAndNotify

0000000000152bb6 t __NSSetFloatValueForKeyInIvar

0000000000152712 t __NSSetFloatValueForKeyWithMethod

00000000000c9ba9 t __NSSetIntValueAndNotify

0000000000152c0f t __NSSetIntValueForKeyInIvar

00000000000abefb t __NSSetIntValueForKeyWithMethod

00000000001817a7 T __NSSetLogCStringFunction

00000000000a5389 t __NSSetLongLongValueAndNotify

0000000000152d5f t __NSSetLongLongValueForKeyInIvar

00000000000a55e1 t __NSSetLongLongValueForKeyWithMethod

000000000015abd7 t __NSSetLongValueAndNotify

0000000000152cb7 t __NSSetLongValueForKeyInIvar

0000000000152779 t __NSSetLongValueForKeyWithMethod

000000000011651f T __NSSetMainBundle

00000000001531ad t __NSSetNilValueForKey

00000000000647b0 t __NSSetObjectSetIvarValueForKeyInIvar

0000000000096e38 t __NSSetObjectValueAndNotify

0000000000028b2d t __NSSetObjectValueForKeyInIvar

00000000000c0657 t __NSSetPointValueAndNotify

0000000000152e5f t __NSSetPointValueForKeyInIvar

00000000000c05f1 t __NSSetPointValueForKeyWithMethod

000000000015b067 t __NSSetRangeValueAndNotify

0000000000152ebc t __NSSetRangeValueForKeyInIvar

00000000001528b2 t __NSSetRangeValueForKeyWithMethod

0000000000099610 t __NSSetRectValueAndNotify

0000000000152f15 t __NSSetRectValueForKeyInIvar

000000000015291d t __NSSetRectValueForKeyWithMethod

000000000015ae1f t __NSSetShortValueAndNotify

0000000000152db3 t __NSSetShortValueForKeyInIvar

0000000000152849 t __NSSetShortValueForKeyWithMethod

00000000000af692 t __NSSetSizeValueAndNotify

0000000000152f86 t __NSSetSizeValueForKeyInIvar

00000000000af62d t __NSSetSizeValueForKeyWithMethod

000000000015aab3 t __NSSetUnsignedCharValueAndNotify

0000000000152b09 t __NSSetUnsignedCharValueForKeyInIvar

00000000001526a9 t __NSSetUnsignedCharValueForKeyWithMethod

00000000000e59a2 t __NSSetUnsignedIntValueAndNotify

0000000000152c63 t __NSSetUnsignedIntValueForKeyInIvar

0000000000103f5e t __NSSetUnsignedIntValueForKeyWithMethod

0000000000096fa8 t __NSSetUnsignedLongLongValueAndNotify

00000000000f5f93 t __NSSetUnsignedLongLongValueForKeyInIvar

00000000000c745d t __NSSetUnsignedLongLongValueForKeyWithMethod

000000000015acfa t __NSSetUnsignedLongValueAndNotify

0000000000152d0b t __NSSetUnsignedLongValueForKeyInIvar

00000000001527e1 t __NSSetUnsignedLongValueForKeyWithMethod

000000000015af43 t __NSSetUnsignedShortValueAndNotify

0000000000152e09 t __NSSetUnsignedShortValueForKeyInIvar

0000000000103ef5 t __NSSetUnsignedShortValueForKeyWithMethod

 

Foundation 提供了大部分基础数据类型的辅助函数,此外还包括一些常见的 Cocoa 结构体如 Point, Range, Rect, Size,这表明这些结构体也可以用于自动键值观察,但要注意除此之外的结构体就不能用于自动键值观察了。

 

 

 

待续...........

 

 demo:https://github.com/easyui/TestKVO

 

 

 参考:

http://blog.csdn.net/wzzvictory/article/details/9674431?utm_source=tuicool

http://blog.csdn.net/kesalin/article/details/8194240

http://www.cnblogs.com/dark-angel/archive/2011/05/05/2037734.html

http://objccn.io/issue-7-3/#key-value-validation

http://www.cnblogs.com/496668219long/p/4470923.html

http://www.jianshu.com/p/e036e53d240e

http://blog.sina.com.cn/s/blog_8a38e5240100u2yw.html

http://www.androiddev.net/kvo/

http://nshipster.com/key-value-observing/

http://tech.glowing.com/cn/implement-kvo/

https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVOImplementation.html

2018-04-22 03:53:16 weixin_34396103 阅读数 7
  • iOS从初级到精通就业 Objective-C

    C语言基础入门,面向初级,毫无编程经验的学生群体,通过学习能够掌握C语言的基本语法,编程的基本概念,培养自己的编程思维!OC语言详解,掌握OC核心内容(面向对象、内存管理,类的扩展,KVC和KVO,闭包和常见的Foundation类库的使用方法等,为后续学习iOS SDK打好基础。

    10123 人正在学习 去看看 栾斌

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编码(swift )

阅读数 20

第十三章 KVC和KVO

阅读数 301

浅谈KVO

阅读数 14

没有更多推荐了,返回首页