KVO

1、運用鍵值觀察KVO數組

關鍵是運用一下的幾個函數:
函數

(1)註冊和取消觀察;atom

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

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

(2)處理消息變動;component

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

(3)手動觀察鍵值必須使用的兩個category方法;server

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

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

下面解釋下手動觀察鍵值。


2、手動觀察鍵值

關鍵實現這兩個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


3、鍵值依賴鍵觀察

有時候一個屬性的值依賴於另外一對象中的一個或多個屬性,若是這些屬性中任一屬性的值發生變動,被依賴的屬性值也應當爲其變動進行標記。所以,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

相關文章
相關標籤/搜索