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