- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
複製代碼
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
複製代碼
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;
複製代碼
參數說明: observer
:觀察者,通常傳self
。 keyPath
:路徑,傳入觀察對象的屬性變量,(成員變量不會生效,由於成員變量沒有setter方法)。 options
:枚舉,一共有4個值,最經常使用的爲NSKeyValueObservingOptionNew
。bash
NSKeyValueObservingOptionNew 新值
NSKeyValueObservingOptionOld 舊值
NSKeyValueObservingOptionInitial 觀察最初的值(在註冊觀察服務時會調用一次觸發方法)
NSKeyValueObservingOptionPrior 分別在值修改先後觸發方法(即一次修改有兩次觸發)
複製代碼
context
:打上標籤,在觀察回調中可用於區分,防止繼承。object
也能夠達到相似的功能,但object
須要遍歷不少東西,context
性能更好。不想用的話就傳入NULL
。 object
:被觀察的對象。打印出來是<Person: 0x600001815710>
。 change
:發生改變的內容。性能
####KVO的基本用法 1.建立一個類,命名Person
,分別添加成員變量與屬性變量。ui
@interface Person : NSObject{
NSString *nickName;
}
@property(strong,nonatomic)NSString * name;
@property(assign,nonatomic)int age;
@property(strong,nonatomic)NSMutableArray * cars;
@end
複製代碼
2.在控制器中,建立Person
對象,給對象添加觀察者。atom
@property(strong,nonatomic)Person * p;
self.p = [[Person alloc]init];
[self.p addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:NULL];
[self.p addObserver:self forKeyPath:@"age" options:(NSKeyValueObservingOptionNew) context:NULL];
複製代碼
3.添加觀察回調spa
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"改變的內容===%@", change);
NSLog(@"觀察的對象===%@", object);
}
複製代碼
4.移除觀察者(必須),不移除有可能會致使崩潰。3d
-(void)dealloc{
[self.p removeObserver:self forKeyPath:@"name"];
}
複製代碼
5.觸發監聽(舉例)code
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.p.name = @"尤先森";
self.p.age ++;
[[self.p mutableArrayValueForKey:@"cars"] addObject:@"阿斯頓馬丁"];
}
複製代碼
key
等於name
的時候,纔會觸發觀察回調。+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
if ([key isEqualToString:@"name"]) {
return YES;
}
return NO;
}
複製代碼
-(void)setAge:(int)age{
[self willChangeValueForKey:@"age"];
_age = age;
[self didChangeValueForKey:@"age"];
}
複製代碼
// 1.新建3個屬性
@property(assign,nonatomic)double done; //已完成
@property(assign,nonatomic)double total; //總量
@property(strong,nonatomic)NSString *downloadProgress; //下載進度
// 2.關聯屬性
+(NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:@"downloadProgress"]) {
NSArray *array = @[@"done",@"total"];
keyPaths = [keyPaths setByAddingObjectsFromArray:array];
}
return keyPaths;
}
// 3.添加監聽
[self.p addObserver:self forKeyPath:@"downloadProgress" options:(NSKeyValueObservingOptionNew) context:NULL];
// 4.觸發
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.p.done +=10;
}
//downloadProgress懶加載
-(NSString *)downloadProgress{
if (self.done == 0) {
_done = 10;
}
if (self.total == 0) {
_total = 100;
}
return [NSString stringWithFormat:@"%f",1.0f*self.done/self.total];
}
複製代碼
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//按照通常套路將不會回調
[self.p.cars addObject:@"蘭博基尼"];
//KVO 中 ,對集合類型(Array,set)操做。
//取值和賦值過程 跟通常狀況不同,須要經過KVC 的方式。
[[self.p mutableArrayValueForKey:@"cars"] addObject:@"蘭博基尼"];
}
複製代碼
因爲KVO並無開源,爲了瞭解KVO內部的實現原理,嘗試 探索KVO底層原理orm