KVO

一、运用键值观察KVO

关键是运用一下的几个函数:

(1)注册和取消观察;

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;

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

(2)处理消息变更;

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;

(3)手动观察键值必须使用的两个category方法;

- (void)willChangeValueForKey:(NSString *)key;

- (void)didChangeValueForKey:(NSString *)key;

下面解释下手动观察键值。


二、手动观察键值

关键实现这两个category方法:- (void)willChangeValueForKey:(NSString *)key;- (void)didChangeValueForKey:(NSString *)key;

首先,需要手动实现属性的 setter 方法,并在设置操作的前后分别调用 willChangeValueForKey: 和 didChangeValueForKey方法,这两个方法用于通知系统该 key 的属性值即将和已经变更了;

其次,要实现类方法 automaticallyNotifiesObserversForKey,并在其中设置对该 key 不自动发送通知(返回 NO 即可)。这里要注意,对其它非手动实现的 key,要转交给 super 来处理。

//+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
//    
//    if ([key isEqualToString:@"age"]) {
//        
//        return NO;
//    }
//    
//    else if ([key isEqualToString:@"number"]) {
//        
//        return YES;
//    }
//    
//    else if ([key isEqualToString:@"testst"]) {
//        
//        return YES;
//    }
//
//    
//    return [super automaticallyNotifiesObserversForKey:key];
//}



- (NSString *)number {
    
    return theNumber;
}



- (void)setNumber:(NSString *)number {
    
    //手动调用KVO
    [self willChangeValueForKey:@"number"];
    
    theNumber = number;
    
    //手动调用KVO
    [self didChangeValueForKey:@"number"];
}



//- (void)willChangeValueForKey:(NSString *)key {
//    
//    if (![key isEqualToString:@"number"]) {
//        
//        NSLog(@"old value -- %@",[self valueForKey:@"number"]);
//    }
//}
//
//
//
//- (void)didChangeValueForKey:(NSString *)key {
//    
//    if (![key isEqualToString:@"number"]) {
//        
//        NSLog(@"new value -- %@",[self valueForKey:@"number"]);
//    }
//}

重写了- (void)willChangeValueForKey:(NSString *)key;- (void)didChangeValueForKey:(NSString *)key;就不会再执行- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;不管automaticallyNotifiesObserversForKey返回的是不是YES(并不是很理解为什么会这样)。

看一个例子:

[user addObserver:self forKeyPath:@"number" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"test"];
    
    //[others addObserver:self forKeyPath:@"delegate" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)([OtherObject class])];
    
    
    user.number = @"1000002";

model中手动调用KVO;[self willChangeValueForKey:@"number"];  [self didChangeValueForKey:@"number"];

(1)若都调用,则会观察两遍observeValueForKeyPath:ofObject:change:context:

2015-12-10 10:07:55.907 Mannyi1[1408:326320]  old value is -- 3423

2015-12-10 10:07:55.908 Mannyi1[1408:326320]  new value is -- 1000002

2015-12-10 10:07:55.908 Mannyi1[1408:326320]  old value is -- 3423

2015-12-10 10:07:55.908 Mannyi1[1408:326320]  new value is -- 1000002

(2)调用一个函数或者都不调用,则只观察一遍observeValueForKeyPath:ofObject:change:context:

2015-12-10 10:05:25.469 Mannyi1[1344:314649]  old value is -- 3423

2015-12-10 10:05:25.469 Mannyi1[1344:314649]  new value is -- 1000002


三、键值依赖键观察

有时候一个属性的值依赖于另一对象中的一个或多个属性,如果这些属性中任一属性的值发生变更,被依赖的属性值也应当为其变更进行标记。因此,object 引入了依赖键。

(一)observeValueForKeyPath:ofObject:change:context:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    
    if ([keyPath isEqualToString:@"delegate"]) {
        
        //NSLog(@"chage value -- %@",change[NSKeyValueChangeNewKey]);
        
        NSLog(@" old information is %@", [change objectForKey:@"old"]);
        NSLog(@" new information is %@", [change objectForKey:@"new"]);
    }
    
    else if ([keyPath isEqualToString:@"number"]) {
        
        NSLog(@" old value is -- %@",change[NSKeyValueChangeOldKey]);
        NSLog(@" new value is -- %@",change[NSKeyValueChangeNewKey]);
    }
    
    else {
        
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

这个处理变更通知的函数内,可以处理多个keyPath。


(二)实现依赖

(1)被依赖的Model(number、name)属性。

#import <Foundation/Foundation.h>

@interface TestObject : NSObject
{
//    NSString *testst;
}


//- (void)setAge:(int)theAge;


@property (nonatomic,copy) NSString *number;
@property (nonatomic,copy) NSString *name;
@end


#import "TestObject.h"


@interface TestObject ()
{
//    int age;
//    NSString *theNumber;
}


//- (int)age;

@end

@implementation TestObject

@synthesize number;
@synthesize name;



- (instancetype)init {
    
    self = [super init];
    if (self) {
        
        number = @"3423";
        name = @"zhang";
    }
    
    return self;
}
@end


(2)实现依赖属性

#import <Foundation/Foundation.h>

@class TestObject;

@interface OtherObject : NSObject
{
//    @private
//    
//    TestObject *_obj;
}

@property (nonatomic,copy) NSString *delegate;
@property (nonatomic,strong) TestObject *obj;

- (instancetype)initWithObj:(TestObject *)obj;

@end


#import "TestObject.h"
#import "OtherObject.h"

@implementation OtherObject



- (instancetype)initWithObj:(TestObject *)obj {
    
    self = [super init];
    
    if (self) {
        
       self.obj = obj;
    }
    
    return self;
}


- (NSString *)delegate {
    
    return [[NSString alloc] initWithFormat:@"%@-%@",self.obj.number,self.obj.name];
}


- (void)setDelegate:(NSString *)delegate {
    
    
    NSArray * array = [delegate componentsSeparatedByString:@"-"];
    [self.obj setNumber:[array objectAtIndex:0]];
    [self.obj setName:[array objectAtIndex:1]];
}

//这个返回的是依赖其他对象的属性名,返回数组。
+ (NSSet *)keyPathsForValuesAffectingDelegate {
    
    
    NSSet * keyPaths = [NSSet setWithObjects:@"obj.number", @"obj.name", nil];
    return keyPaths;
}
@end

这一步关键的是重写+ (NSSet *)keyPathsForValuesAffectingDelegate,返回依赖keyPath(被依赖类对象.属性名)数组。


(3)实例

user = [[TestObject alloc] init];   
others = [[OtherObject alloc] initWithObj:user];

[others addObserver:self forKeyPath:@"delegate" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)([OtherObject class])];
     
user.number = @"1000002";
user.name = @"1000003";

[others removeObserver:self forKeyPath:@"delegate" context:(__bridge void * _Nullable)([OtherObject class])];

得到的结果:

old information is 3423-zhang

2015-12-08 15:31:15.885 Mannyi1[3618:1549002]  new information is 1000002-zhang

2015-12-08 15:31:17.255 Mannyi1[3618:1549002]  old information is 1000002-zhang

2015-12-08 15:31:17.255 Mannyi1[3618:1549002]  new information is 1000002-1000003


你可能感兴趣的:(KVO)