KVO的用法、底層實現原理

KVO的用法spa

KVO也就是key-value-observing(即鍵值觀察),利用一個key來找到某個屬性並監聽其值得改變。用法以下:指針

  • 添加觀察者
  • 在觀察者中實現監聽方法,observeValueForKeyPath: ofObject: change: context:(經過查閱文檔能夠知道,絕大多數對象都有這個方法,由於這個方法屬於NSObject)
  • 移除觀察者
//讓對象b監聽對象a的name屬性
//options屬性能夠選擇是哪一個
 /* NSKeyValueObservingOptionNew =0x01, 新值 
  * NSKeyValueObservingOptionOld =0x02, 舊值 
  */ 
[a addObserver:b forKeyPath:@"name"options:kNilOptionscontext:nil]; 
a.name = @"zzz";
#pragma mark - 實現KVO回調方法
/* * 當對象的屬性發生改變會調用該方法
    * @param keyPath 監聽的屬性 
    * @param object 監聽的對象 
    * @param change 新值和舊值 
    * @param context 額外的數據 
*/
- (void)observeValueForKeyPath:(NSString *)keyPathofObject:(id)objectchange:(NSDictionary<NSString *,id>*)change context:(void *)context{ 
    NSLog(@"%@的值改變了,",keyPath); 
    NSLog(@"change:%@", change);
}
//最後不要忘記了,去移除observer
- (void)dealloc{ 
       [a removeObserver:b forKeyPath:@"name"];
  }
KVO鍵值觀察者底層解析
涉及到了runtime,關於isa指針
手動實現鍵值觀察(代碼示例)

被觀察的對象Target(重寫setter/getter方法)
Target.hcode

@interface Target : NSObject
{
   int age;
}
// for manual KVO 
- age- (int) age;
- (void) setAge:(int)theAge;
@end

 

 

Target.mserver

@implementation Target
- (id) init{ 
    self = [super init]; 
    if (nil != self) { 
          age = 10; 
     } 
    return self;
}
// for manual KVO - age
- (int) age{
    return age;
}
- (void) setAge:(int)theAge{ 
    [self willChangeValueForKey:@"age"];
    age = theAge; 
    [self didChangeValueForKey:@"age"];
}
+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key { 
    if ([key isEqualToString:@"age"]) {
     return NO;
 } 
return [super automaticallyNotifiesObserversForKey:key];
}
@end

 

首先,須要手動實現屬性的 setter 方法,並在設置操做的先後分別調用 willChangeValueForKey: didChangeValueForKey方法,這兩個方法用於通知系統該 key 的屬性值即將和已經變動了;
其次,要實現類方法 automaticallyNotifiesObserversForKey,並在其中設置對該 key 不自動發送通知(返回 NO 便可)。這裏要注意,對其它非手動實現的 key,要轉交給 super 來處理。對象

實現原理

KVO的實現是基於runtime運行時的,下面就來詳細介紹一下原理:仍是這張圖:blog


 
  • 當某個類的對象第一次被觀察時,系統就會在運行期動態地建立該類的一個派生類,在這個派生類中重寫基類中任何被觀察屬性的 setter 方法。
  • 派生類在被重寫的 setter 方法中實現真正的通知機制,就如前面手動實現鍵值觀察那樣。這麼作是基於設置屬性會調用 setter 方法,而經過重寫就得到了 KVO 須要的通知機制。固然前提是要經過遵循 KVO 的屬性設置方式來變動屬性值,若是僅是直接修改屬性對應的成員變量,是沒法實現 KVO 的。
  • 同時派生類還重寫了 class 方法以「欺騙」外部調用者它就是起初的那個類。而後系統將這個對象的 isa 指針指向這個新誕生的派生類,所以這個對象就成爲該派生類的對象了,於是在該對象上對 setter 的調用就會調用重寫的 setter,從而激活鍵值通知機制。此外,派生類還重寫了 dealloc 方法來釋放資源。

 

KVO與Notification之間的區別:ip

notification是須要一個發送notification的對象,通常是notificationCenter,來通知觀察者。資源

KVO是直接通知到觀察對象,而且邏輯很是清晰,實現步驟簡單。rem

相關文章
相關標籤/搜索