Key-Value Observing (键值监测)

Key-Value Observing (键值监测)

简介

KVO是一套当目标对象的属性值改变时观察者对象能够接受到通知的机制。必须先理解KVC才能更好的理解KVO,前者是后者的实现基础。

这样的通信机制在MVC设计模式很是常见

实现过程简单来说分为3步:

1、添加观察这和监测对象

2、监测对象改变

3、收到值改变通知,处理后续逻辑

举个生活中的例子就是给银行卡开通短信通知的业务,总体也是分3步“

1、去银行办理短信业务

2、账号财产变动

3、收到短信通知

KVO是框架级别的服务,无需自己发送通知,使用方便,基本不需要添加额外代码即可使用。

详情

为了使用KVO,必须满足以下3步

1、目标对象的属性,必须支持KVO

2、注册观察者与被观察者addObserver:forKeyPath:options:context:

3、观察者必须实现observeValueForKeyPath:ofObject:change:context:方法

第一步、确保目标支持KVO

被监测的目标对象的属性支持KVO必须满足以下条件:

1、目标对象的属性必须支持KVC,对于1对1属性简单来说就是实现set和get方法。详情和1对多请阅读官方说明。系统已有类及子类自动支持,放心使用。

2、自动和手动属性通知

目标对象必须能发出属性变化通知。系统默认支持,也可自定义。

系统默认支持,且支持的很好,一般无需自定义。

//如果需要自定义,需要重新此方法,默认返回YES
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key;

//在set方法中手动调用,变化类型只是针对NSKeyValueChangeSetting
- (void)willChangeValueForKey:(NSString *)key;
- (void)didChangeValueForKey:(NSString *)key;

例如

//假设有属性
@property (nonatomic,copy)NSString *name;

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {

    BOOL automatic = NO;
    /* 只自定义指定属性,其它仍然自动发送通知 */
    if ([theKey isEqualToString:@"name"])
    {
        //在set方法中手动调用相关方法
        automatic = NO;
    }
    else
    {
        //此方法默认返回YES
        automatic = [super automaticallyNotifiesObserversForKey:theKey];
    }
    return automatic;
}

- (void)setName:(NSString *)name
{
    //即将变化
    [self willChangeValueForKey:@"name"];

    _name = name;

    //已经变化
    [self didChangeValueForKey:@"name"];
}

//如果说只有值不相等时才发送通知,提升性能
- (void)setName:(NSString *)name
{
    if (![name isEqualToString:_name])
    {

        [self willChangeValueForKey:@"name"];

        _name = name;

        [self didChangeValueForKey:@"name"];
    }
}

如果涉及1对多的容器类,需要自己实现 NSKeyValueChangeInsertion, NSKeyValueChangeRemoval, NSKeyValueChangeReplacement三种操作对应的方法,例如

//Keys为属性名称
- (void)removeKeysAtIndexes:(NSIndexSet *)indexes {
    [self willChange:NSKeyValueChangeRemoval
        valuesAtIndexes:indexes forKey:@"keys"];

    // Remove the transaction objects at the specified indexes.

    [self didChange:NSKeyValueChangeRemoval
        valuesAtIndexes:indexes forKey:@"keys"];
}

3、属性依赖

如果目标对象属性存在依赖关系,注册合适的依赖Keys。核心方法为

第一种、
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key NS_AVAILABLE(10_5, 2_0);
说明:
1、返回目标属性依赖属性的KeyPath的Set。当对象注册后,KVO自动监测该对象所有的KeyPaths。
2、其默认实现从对象所属类的方法列表中匹配方法:+keyPathsForValuesAffecting<Key>(<Key>为属性名,比如Name),如果存在执行并返回结果;如果不存在向底层寻找已经废弃的方法+setKeys:triggerChangeNotificationsForDependentKey:
3、可以用来替换手动调用-willChangeValueForKey:/-didChangeValueForKey:来实现属性依赖的解决方案
4、不能在已有类的Category中使用,在Category禁止重写此方法,可以使用+keyPathsForValuesAffecting<Key>来实现。

第二种、
或者重写此格式+keyPathsForValuesAffecting<Key>(<Key>为属性名,比如Name)的方法名

比如说,姓名=姓+名;当二者任一变动时更新姓名

@property (nonatomic,copy)NSString *name;//姓名
@property (nonatomic,copy)NSString *firstName;//姓
@property (nonatomic,copy)NSString *lastName;//名

//重新get方法,表明字段组成
-(NSString *)name
{
    return [NSString stringWithFormat:@"%@%@",_firstName,_lastName];
}

//通过此方法
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
 {

    NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];

    if ([key isEqualToString:@"name"])
    {
        NSArray *affectingKeys = @[@"lastName", @"firstName"];
        keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
    }
    return keyPaths;
}
或者
+ (NSSet *)keyPathsForValuesAffectingName
{
    return [NSSet setWithObjects:@"lastName", @"firstName", nil];
}

//改变值
- (void)viewDidLoad {
    [super viewDidLoad];

    [self setValue:@"张" forKey:@"firstName"];
    [self setValue:@"三" forKey:@"lastName"];

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

    //名称改变,刷新姓名
    [self setValue:@"龙" forKey:@"lastName"];

}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    NSLog(@"NSKeyValueChangeOldKey:%@",change[NSKeyValueChangeOldKey]);
    NSLog(@"NSKeyValueChangeNewKey:%@",change[NSKeyValueChangeNewKey]);
}

//输出结果
2016-09-06 17:05:01.904 KVC[4192:307124] NSKeyValueChangeOldKey:张三
2016-09-06 17:05:01.904 KVC[4192:307124] NSKeyValueChangeNewKey:张龙

注意:以上关于属性依赖的处理方法不支持一对多的关系。比如说ViewController对象有一个totalNumber表示总数和属性datas数组,数组中Data的对象,对象含有number属性。

/* Data对象 */
@interface Data : NSObject

@property (nonatomic,assign)NSInteger number;

@end

/* ViewController对象 */
@interface ViewController ()

@property (nonatomic,assign)NSInteger totalNumber;

@property (nonatomic,strong)NSArray *datas;

@end

可以采用以下方法解决

1、注册每个Data对象的年龄属性为监测属性,ViewController对象为观察者,data.number变化时,使用集合运算符求和,然后设置ViewController的totalNumber属性值

- (void)viewDidLoad {
    [super viewDidLoad];

    /* 创建Data对象 */
    Data * data1 = [[Data alloc] init];
    data1.number = 0;

    Data *data2 = [[Data alloc] init];
    data2.number = 0;

    Data *data3 = [[Data alloc] init];
    data3.number = 0;

    /* self.datas属性赋值 */
    [self setValue:@[data1,data2,data3] forKey:@"datas"];

    /* 监测self.datas中每个data对象的number属性 */
    //(0, 3) 中0表示index从0开始,0表示长度3,也就是index(0、1、2);但是不能使得self.datas数组越界
    NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 3)];

    [self.datas addObserver:self toObjectsAtIndexes:set forKeyPath:@"number" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];

    /* 监测totalNumber属性 */
    [self addObserver:self forKeyPath:@"totalNumber" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];

    /* 改变值,查看输出结果 */
    [data1 setValue:@"1" forKey:@"number"];
    [data2 setValue:@"1" forKey:@"number"];
    [data3 setValue:@"1" forKey:@"number"];

}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"number"])
    {
        [self updateTotalNumber];
    }
    else if ([keyPath isEqualToString:@"totalNumber"])
    {
        NSLog(@"%@--%@",keyPath,change);
    }
}

//更新总数
- (void)updateTotalNumber
{

    NSNumber *total = [self valueForKeyPath:@"[email protected]"];

    [self setValue:total forKey:@"totalNumber"];
}

//输出结果
2016-09-07 14:10:10.694 KVC[3034:165515] totalNumber--{
    kind = 1;
    new = 1;
    old = 0;
}
2016-09-07 14:10:10.695 KVC[3034:165515] totalNumber--{
    kind = 1;
    new = 2;
    old = 1;
}
2016-09-07 14:10:10.695 KVC[3034:165515] totalNumber--{
    kind = 1;
    new = 3;
    old = 2;
}

以上代码中不涉及移除,根据需要添加代码,对象delloc之前,必须移除。

2、Core Data,自动实现类似的功能。

第二步、注册

1、注册通知

为了能够获取目标属性值改变的通知,需要注册观察者和观察对象属性

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
参数说明:
observer:观察者对象,就是想收到变动通知的对象
keyPath:监测的目标属性的路径
options:决定了通知中内容和发送时间
context:C指针或者对象,传递参数,一般不用传NULL

例如在新建的工程的ViewController中

@property (nonatomic,copy)NSString *name;

- (void)registerAsObserver
{

    [self addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];

}

注意:此方法不持有观察者对象、被观察对象、context,管理好其生命周期。

2、接受通知

当监测的目标对象的属性变化时,观察者将调用observeValueForKeyPath:ofObject:change:context: message,所有的观察者都必须实现此方法

- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString*, id> *)change context:(nullable void *)context;
keyPath:监测的目标属性的路径
object:观察者对象
change:变化内容
context:C指针或者对象,传递参数,一般不用传NULL

3、移除通知

当不再使用时,需要通过以下方法移除通知。

- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context NS_AVAILABLE(10_7, 5_0);

- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

keyPath:监测的目标属性的路径
observer:观察者对象
context:C指针或者对象,传递参数,一般不用传NULL
以上两个方法,根据需要选择使用。

特别注意:NSArray、NSOrderedSet、NSSet不支持以上三个方法,调用会抛出异常。

第三步、属性变化

使用KVC方法,或者能够触发KVC方法使得监测的目标对象属性变化。

第四步、接收变化

当监测的目标对象的属性变化时,观察者将调用observeValueForKeyPath:ofObject:change:context: message,所有的观察者都必须实现此方法。在此方法中处理变化

以上第二、三、四步组成一次完整的KVO使用过程,下边关于一些参数的用法说明

参数说明

关于NSKeyValueObservingOptions

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
options:决定了通知中内容和发送时间

NSKeyValueObservingOptions是一个枚举类型

typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) {

    /*通知dic中是否包含新值*/
    NSKeyValueObservingOptionNew = 0x01,

    /*通知dic中是否包含新值*/
    NSKeyValueObservingOptionOld = 0x02,

    /*添加此操作,通知dic中是否包含注册通知前的初始值;如果目标属性是容器类,每个元素都会触发通知发送*/
    NSKeyValueObservingOptionInitial NS_ENUM_AVAILABLE(10_5, 2_0) = 0x04,

    /*添加此操作,每次值变化,将触发两次:1、变化前(dic中包含NSKeyValueChangeNotificationIsPriorKey,值为1,NSNumber类型)
    2、变化后
    */
    NSKeyValueObservingOptionPrior NS_ENUM_AVAILABLE(10_5, 2_0) = 0x08
};

关于Change Dictionary

- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString*, id> *)change context:(nullable void *)context;
change:变化内容

其包含以下几种内容,可以使用以下字段取值

//值变化类型
FOUNDATION_EXPORT NSString *const NSKeyValueChangeKindKey;

//新值
FOUNDATION_EXPORT NSString *const NSKeyValueChangeNewKey;

//旧值
FOUNDATION_EXPORT NSString *const NSKeyValueChangeOldKey;

//容器类中,变化值所在位置,NSIndexSet类型
FOUNDATION_EXPORT NSString *const NSKeyValueChangeIndexesKey;

//是否值变化前,NSNumber类型
FOUNDATION_EXPORT NSString *const NSKeyValueChangeNotificationIsPriorKey NS_AVAILABLE(10_5, 2_0);

其中NSKeyValueChangeKindKey有以下几种类型

typedef NS_ENUM(NSUInteger, NSKeyValueChange)
{
    NSKeyValueChangeSetting = 1,//值变化
    NSKeyValueChangeInsertion = 2,//插入
    NSKeyValueChangeRemoval = 3,//移除
    NSKeyValueChangeReplacement = 4,//替换
};

上边的NSKeyValueChangeKindKey2、3、4分别对应着有序集合比如NSArray中增、删、改操作。

参数说明-代码示例

1、NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld

@property (nonatomic,copy)NSString *name;

//注册
- (void)viewDidLoad {
    [super viewDidLoad];

    [self setValue:@"zwq" forKey:@"name"];

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

   [self setValue:@"zwq2" forKey:@"name"];

}

//接收
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    NSLog(@"NSKeyValueChangeKindKey:%@",change[NSKeyValueChangeKindKey]);
    NSLog(@"NSKeyValueChangeOldKey:%@",change[NSKeyValueChangeOldKey]);
    NSLog(@"NSKeyValueChangeNewKey:%@",change[NSKeyValueChangeNewKey]);
    NSLog(@"change dic:%@",change);
}

//输出结果
2016-09-06 14:58:55.349 KVC[3614:231751] NSKeyValueChangeKindKey:1
2016-09-06 14:58:55.351 KVC[3614:231751] NSKeyValueChangeOldKey:zwq
2016-09-06 14:58:55.351 KVC[3614:231751] NSKeyValueChangeNewKey:zwq2
2016-09-06 14:58:55.352 KVC[3614:231751] change dic:{
    kind = 1;
    new = zwq2;
    old = zwq;
}

从以上代码可以看出change中的key如何取值,以及其中内容。

2、NSKeyValueObservingOptionInitial

@property (nonatomic,copy)NSString *name;

//注册
- (void)viewDidLoad {
    [super viewDidLoad];

    [self setValue:@"zwq" forKey:@"name"];

    //为了明显观察值变化,多添加两个key
    [self addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];

    [self setValue:@"zwq2" forKey:@"name"];

}

//接收
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    NSLog(@"NSKeyValueChangeKindKey:%@",change[NSKeyValueChangeKindKey]);
    NSLog(@"NSKeyValueChangeOldKey:%@",change[NSKeyValueChangeOldKey]);
    NSLog(@"NSKeyValueChangeNewKey:%@",change[NSKeyValueChangeNewKey]);
    NSLog(@"change dic:%@",change);
}

//输出结果
2016-09-06 15:06:49.963 KVC[3654:237675] NSKeyValueChangeKindKey:1
2016-09-06 15:06:49.964 KVC[3654:237675] NSKeyValueChangeOldKey:(null)
2016-09-06 15:06:49.964 KVC[3654:237675] NSKeyValueChangeNewKey:zwq
2016-09-06 15:06:49.964 KVC[3654:237675] change dic:{
    kind = 1;
    new = zwq;
}

2016-09-06 15:06:49.964 KVC[3654:237675] NSKeyValueChangeKindKey:1
2016-09-06 15:06:49.965 KVC[3654:237675] NSKeyValueChangeOldKey:zwq
2016-09-06 15:06:49.965 KVC[3654:237675] NSKeyValueChangeNewKey:zwq2
2016-09-06 15:06:49.965 KVC[3654:237675] change dic:{
    kind = 1;
    new = zwq2;
    old = zwq;
}

对比1、2的输出结果,可以看出2中可以获得初始值,后续值变化接收到的通知dic内容同1中一样(可以多改变几次赋值,观察结果)

3、NSKeyValueObservingOptionPrior

@property (nonatomic,copy)NSString *name;

//注册
- (void)viewDidLoad {
    [super viewDidLoad];

    [self setValue:@"zwq" forKey:@"name"];

    //为了明显观察值变化,多添加两个key
    [self addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionPrior|NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];

    [self setValue:@"zwq2" forKey:@"name"];
    [self setValue:@"zwq3" forKey:@"name"];

}

//接收
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    NSLog(@"NSKeyValueChangeKindKey:%@",change[NSKeyValueChangeKindKey]);
    NSLog(@"NSKeyValueChangeOldKey:%@",change[NSKeyValueChangeOldKey]);
    NSLog(@"NSKeyValueChangeNewKey:%@",change[NSKeyValueChangeNewKey]);
    NSLog(@"NSKeyValueChangeNotificationIsPriorKey:%@",change[NSKeyValueChangeNotificationIsPriorKey]);
    NSLog(@"change dic:%@",change);
}

//输出结果
2016-09-06 15:17:16.325 KVC[3730:246941] NSKeyValueChangeKindKey:1
2016-09-06 15:17:16.326 KVC[3730:246941] NSKeyValueChangeOldKey:zwq
2016-09-06 15:17:16.326 KVC[3730:246941] NSKeyValueChangeNewKey:(null)
2016-09-06 15:17:16.326 KVC[3730:246941] NSKeyValueChangeNotificationIsPriorKey:1
2016-09-06 15:17:16.327 KVC[3730:246941] change dic:{
    kind = 1;
    notificationIsPrior = 1;
    old = zwq;
}
2016-09-06 15:17:16.327 KVC[3730:246941] NSKeyValueChangeKindKey:1
2016-09-06 15:17:16.327 KVC[3730:246941] NSKeyValueChangeOldKey:zwq
2016-09-06 15:17:16.327 KVC[3730:246941] NSKeyValueChangeNewKey:zwq2
2016-09-06 15:17:16.327 KVC[3730:246941] NSKeyValueChangeNotificationIsPriorKey:(null)
2016-09-06 15:17:16.328 KVC[3730:246941] change dic:{
    kind = 1;
    new = zwq2;
    old = zwq;
}

2016-09-06 15:17:16.328 KVC[3730:246941] NSKeyValueChangeKindKey:1
2016-09-06 15:17:16.328 KVC[3730:246941] NSKeyValueChangeOldKey:zwq2
2016-09-06 15:17:16.328 KVC[3730:246941] NSKeyValueChangeNewKey:(null)
2016-09-06 15:17:16.328 KVC[3730:246941] NSKeyValueChangeNotificationIsPriorKey:1
2016-09-06 15:17:16.328 KVC[3730:246941] change dic:{
    kind = 1;
    notificationIsPrior = 1;
    old = zwq2;
}
2016-09-06 15:17:16.328 KVC[3730:246941] NSKeyValueChangeKindKey:1
2016-09-06 15:17:16.329 KVC[3730:246941] NSKeyValueChangeOldKey:zwq2
2016-09-06 15:17:16.329 KVC[3730:246941] NSKeyValueChangeNewKey:zwq3
2016-09-06 15:17:16.329 KVC[3730:246941] NSKeyValueChangeNotificationIsPriorKey:(null)
2016-09-06 15:17:16.329 KVC[3730:246941] change dic:{
    kind = 1;
    new = zwq3;
    old = zwq2;
}

从以上代码输出结果不难看出:a、通知分开发送:变化前和变化后 b、变化前NSKeyValueChangeNewKey为空,但是NSKeyValueChangeNotificationIsPriorKey值为1;变化后反之。

结合以上3段代码结论:4种各有所用,可以单独使用,也可以组合使用,根据需要选择合适。作用简单概括:1.新、旧值 2.初始值 3.值变化前后。

4、NSKeyValueChangeKindKey

@interface ViewController ()

@property (nonatomic,strong)NSArray *datas;
@end

- (void)viewDidLoad {
    [super viewDidLoad];

    /* 创建Data对象 */
    Data * data1 = [[Data alloc] init];
    Data *data2 = [[Data alloc] init];
    Data *data3 = [[Data alloc] init];
    Data *data4 = [[Data alloc] init];

    /* self.datas属性赋值 */
    [self setValue:@[data1,data2,data3] forKey:@"datas"];

    /* 监测self.datas属性 */
    [self addObserver:self forKeyPath:@"datas" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];

    //修改datas数组:增删改
    NSMutableArray *mutable_subdatas = [self mutableArrayValueForKeyPath:@"datas"];
    [mutable_subdatas addObject:data4];
    [mutable_subdatas removeObject:data4];
    [mutable_subdatas replaceObjectAtIndex:2 withObject:data4];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    NSLog(@"%@--%@",keyPath,change);
}

//输出结果
2016-09-07 14:46:50.366 KVC[3231:186568] datas--{
    indexes = "<_NSCachedIndexSet: 0x7f8591428c60>[number of indexes: 1 (in 1 ranges), indexes: (3)]";
    kind = 2;
    new =     (
        "<Data: 0x7f8591427b00>"
    );
}

2016-09-07 14:46:50.367 KVC[3231:186568] datas--{
    indexes = "<_NSCachedIndexSet: 0x7f8591428c60>[number of indexes: 1 (in 1 ranges), indexes: (3)]";
    kind = 3;
    old =     (
        "<Data: 0x7f8591427b00>"
    );
}

2016-09-07 14:46:50.367 KVC[3231:186568] datas--{
    indexes = "<_NSCachedIndexSet: 0x7f8591428c40>[number of indexes: 1 (in 1 ranges), indexes: (2)]";
    kind = 4;
    new =     (
        "<Data: 0x7f8591427b00>"
    );
    old =     (
        "<Data: 0x7f8591402350>"
    );
}
时间: 2024-08-18 13:38:41

Key-Value Observing (键值监测)的相关文章

xcode KVC:Key Value Coding 键值编码

赋值 // 能修改私有成员变量 - (void)setValue:(id)value forKey:(NSString *)key; - (void)setValue:(id)value forKeyPath:(NSString *)keyPath; - (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues; 取值 // 能取得私有成员变量的值 - (id)valueForKey:(NSString *)key; - (

深度理解Key-Value Observing 键值观察

前言   在上一阶段的开发过程中,我们大量使用了 KVO 机制,来确保页面信息的及时同步.也因此碰到了很多问题,促使我们去进一步学习 KVO 的相关机制,再到寻找更好的解决方案.鉴于 KVO 让人欲仙欲死的使用经历,在这里做一个简单分享.此分享的目的,更多的是在于点出 KVO 相关的技术点,供我们大家在学习和使用过程中做一个参考. 对于 KVO 的背后机制感兴趣的同学,可以直接看第三部分,KVC 和 isa-swizzling . 对于 替代方案感兴趣的同学,请直接跳到末尾的第五部分,有列出了目

php 获取数组第一个key 第一个键值对 等等

PHP 获取数组中的第一个元素或最后一个元素的值或者键值可以使用 PHP 自带的数组函数. PHP 获取数组中的第一个元素的值或者键值所使用的函数: current() - 返回数组中当前元素值(即:返回当前被数组内部指针指向的元素的值). reset() - 函数把数组的内部指针指向第一个元素,并返回这个元素的值. key() - 返回数组内部指针指向的元素的索引(即:键值). PHP 获取数组中的最后一个元素的值或者键值所使用的函数: end() - 函数将数组内部指针指向最后一个元素,并返

iOS设计模式之KVO Key-Value Observing 键值观察者模式的简单实用和介绍

它提供一种机制,当指定的对象的属性被修改后,则监听者就会接受到通知. 就好像我们给手机定了一个闹钟,等到了制定的时间,闹钟就会响起,我们就会知道时间到了 这个过程中,我们就是监听者,闹钟就是被监听的对象 我们创建一个student类,使用KVO模式,给其中的username 属性添加监听者(观察者 XSStudent *su = [[XSStudentalloc] init]; su.name = @"zhangsan"; su.age = 12; //Observer 观察者是谁 /

K-V-C 键值观察机制

在两个不同的控制器之间传值是iOS开发中常有的情况,应对这种情况呢,有多种的应对办法.kvc就是其中的一种,所以,我们就在此解释之.   key value observing  键值观察,给人一种高冷的感觉,其实,我们可以用一个通俗的例子来解释之.就拿美俄之间的间谍来举例子.美俄是两个各自独立的国家,但是为了各自的利益,彼此之间勾心斗角,不断的爆出间谍丑闻.打住!从政治的深渊回到技术层面O(∩_∩)O.美国想知道俄罗斯的最新的导弹技术,于是派间谍收集情报,(kvo的第一步:注册观察者-美国,监

Key-Value Observing (键值监測)

Key-Value Observing (键值监測) 简单介绍 KVO是一套当目标对象的属性值改变时观察者对象能够接受到通知的机制.必须先理解KVC才干更好的理解KVO,前者是后者的实现基础. 这种通信机制在MVC设计模式非常是常见 实现过程简单来说分为3步: 1.加入观察这和监測对象 2.监測对象改变 3.收到值改变通知,处理兴许逻辑 举个生活中的样例就是给银行卡开通短信通知的业务,整体也是分3步" 1.去银行办理短信业务 2.账号財产变动 3.收到短信通知 KVO是框架级别的服务.无需自己发

Java中Map根据键值(key)或者值(value)进行排序实现

我们都知道,java中的Map结构是key->value键值对存储的,而且根据Map的特性,同一个Map中 不存在两个Key相同的元素,而value不存在这个限制.换句话说,在同一个Map中Key是唯一的,而value不唯一.Map是一个接口,我们不能 直接声明一个Map类型的对象,在实际开发中,比较常用的Map性数据结构是HashMap和TreeMap,它们都是Map的直接子类.如果考虑到存取 效率的话,建议使用HashMap数据结构,而如果需要考虑到Key的顺序,建议使用TreeMap,但是

浅谈js的键值对key和value

> 昨晚无意中看到类似下面结构的一段代码的取值问题,引起我的兴趣,花了点时间写了个demo给大家分享一下... var obj = [ {"2011":{"name":"jyjin","age":20}}, {"2012":{"name":"jyjin","age":21}} ]; var obj1 = { "2013":

OAF_OAF控件系列 - Key Flexfield键值弹性域的实现(案例)

2014-06-17 BaoXinjian 一.摘要 OAF的弹性域的实现基本和Form的弹性域实现的大体思路是一致的,在注册Table和弹性域完全一样,之后通过控件去实现具体的弹性域显示 Form的弹性域初始化基本都在trigger when-new-form-instance中 OAF的弹性域初始化基本都在processRequest中,或者在processRequest调用AM中的初始化方法 1. 网上的一个例子 新建一个EO,其中需要有相关的CCID字段.这里,如果在EO的Dynamic