zoukankan      html  css  js  c++  java
  • iOS设计模式 —— KV0

     

    刨根问底KVO

    KVO 全称 Key-Value Observing。中文叫键值观察。KVO其实是一种观察者模式,观察者在键值改变时会得到通知,利用它可以很容易实现视图组件和数据模型的分离,当数据模型的属性值改变之后作为监听器的视图组件就会被激发,激发时就会回调监听器自身。相比Notification,KVO更加的简单直接。

    KVO的操作方法由NSKeyValueCoding提供,而他是NSObject的类别,也就是说ObjC中几乎所有的对象都支持KVO操作。

    KVO的使用也很简单,就是简单的3步。

    1. 注册需要观察的对象的属性addObserver:forKeyPath:options:context:
    2. 实现observeValueForKeyPath:ofObject:change:context:方法,这个方法当观察的属性变化时会自动调用.在这个方法中还通过NSKeyValueObservingOptionNew这个参数要求把新值在dictionary中传递过来。
    3. 取消注册观察removeObserver:forKeyPath:context:

    我们观察下代码实现,探究实现原理:

    Person.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #import <Foundation/Foundation.h>

    @interface Person : NSObject

    - (void)registerObserver;

    @property (nonatomic, assign) NSInteger age;

    @end

    Person.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    #import "Person.h"

    @implementation Person

    - (void)registerObserver {

    [self addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
    }

    - (NSString *)description {
    return [NSString stringWithFormat:@"%@,%ld",[self valueForKey:@"isa"],self.age];

    }

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {

    if ([keyPath isEqualToString:@"age"]) {
    NSLog(@"%@",change);
    } else {
    [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
    }

    - (void)dealloc {
    [self removeObserver:self forKeyPath:@"age"];
    }

    main函数

    1
    2
    3
    4
    5
    6
    7
    8
    Person * p = [[Person alloc] init];
    p.age = 20;
    NSLog(@"%@",p);

    [p registerObserver];

    p.age = 30;
    NSLog(@"%@",p);

    运行程序打印出的log日志为:

    1
    2
    3
    4
    5
    6
    7
    Person,20
    {
    kind = 1;
    new = 30;
    old = 20;
    }
    NSKVONotifying_Person,30

    不要太惊讶,我们慢慢解释。

    我重写的Person类的description方法,使用KVC拿到isa指针,获取到self的类名,我们发现在使用KVO之前类名是Person,但注册了KVO之后,类名变成了NSKVONotifying_Person

    主要是因为KVO的实现使用了isa-swizzling。在程序运行时Person会生成一个派生类NSKVONotifying_Person,在这个派生类中重写基类中任何被观察属性的setter方法,用来欺骗系统顶替原先的类。在setter方法中实现真正的通知机制.

    1
    2
    3
    4
    5
    6
    7
    //可以到伪代码

    - (void)setAge:(int)age
    {
    [super setAge:age];
    [监听器 observeValueForKeyPath:@"age" ofObject:self change:@{} context:nil];
    }

    我们又可以猜测,使用KVO,内部一定执行setter方法。

    当我们把上面代码 p.age = 30;改成p->_age = 30;

    你会发现KVO的方法不走了,也证实了这点。

    如果p不提供setAge,getAge方法,还想用KVO.

    1
    2
    3
    [p willChangeValueForKey:@"age"];
    p->_age = 30;
    [p didChangeValueForKey:@"age"];

    苹果官方KVO文档:

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

    另外.....

    我的愿望是.......

    世界和平.........

  • 相关阅读:
    C# 序列化与反序列化之DataContract与xml对子类进行序列化的解决方案
    C# 序列化与反序列化之Binary与Soap无法对泛型List<T>进行序列化的解决方案
    大端小端存储方案
    C# 序列化与反序列化Serialization之Json Xml Binary Soap JavaScript序列化
    C# ctpclient networkstream 使用 BinaryReader的ReadString但是使用streamReader的Readtoend不行
    C# TcpListener TcpClient
    C# System.Net.Mail.MailMessage 发邮件
    C# System.Web.Mail.MailMessage 发邮件
    离线环境下使用二进制方式安装配置Kubernetes集群
    Kubernetes基础:查看状态、管理服务
  • 原文地址:https://www.cnblogs.com/ioshe/p/5937942.html
Copyright © 2011-2022 走看看