1. 程式人生 > >FBKVOController實現原理(簡單描述)

FBKVOController實現原理(簡單描述)

在看這篇文章之前,建議自己寫一個小的FBKVOController Example,如果懶得寫可以在Github上clone我寫的一個非常簡單的example。這樣能建立一個大體的瞭解。

對程式碼中的細節部分沒有做介紹,例如鎖機制,Set,Map這些,只要知道作用即可,不影響對核心程式碼的理解。

程式碼結構:

1.FBKVOController
 對外公開的類,對外提供了初始化,數種監聽的方法。
2._FBKVOInfo
 內部類,用來記錄監聽所需的引數資訊。
3._FBKVOSharedController
內部類,真正實現kvo的類,通過FBKVOController的外部方法呼叫。

內部實現

1.在ViewController中初始化,呼叫。

ViewController.m

@property (nonatomic,strong) FBKVOController * kvoController;
@property (nonatomic,strong) DataModel * model;

.....

self.kvoController = [FBKVOController controllerWithObserver:self];//初始化FBKVOController
    self.model = [[dataView alloc] init];//被監聽的model
[self.kvoController observe:_model keyPath:@"colorString" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial block:^(ViewController * observer, id object, NSDictionary *change) { //some code in block //.... }];

KVOController有兩種監聽方式:一種是block,另一種是用action(或者selector)的方式,上面是block方式。

上面程式碼實現的是監聽_model中的colorString屬性,當該數值初始化(NSKeyValueObservingOptionInitial)或者重新賦值(NSKeyValueObservingOptionNew)時,執行block中的程式碼。

2 .FBKVOController提供的外部方法

- (void)observe:(id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(FBKVONotificationBlock)block
{
  NSAssert(0 != keyPath.length && NULL != block, @"missing required parameters observe:%@ keyPath:%@ block:%p", object, keyPath, block);
  if (nil == object || 0 == keyPath.length || NULL == block) {
    return;
  }

  // create info
  _FBKVOInfo *info = [[_FBKVOInfo alloc] initWithController:self keyPath:keyPath options:options block:block];

  // observe object with info
  [self _observe:object info:info];
}

FBKVOController的監聽方法首先會檢查引數的合法性,如果引數為NULL,則直接return,否則將接受的引數通過// create info 這段程式碼儲存到類info中,然後呼叫方法: - (void)_observe:(id)object info:(_FBKVOInfo *)info 繼續看看這個方法都做了哪些事情。

- (void)_observe:(id)object info:(_FBKVOInfo *)info
{
  // lock
  OSSpinLockLock(&_lock);

  NSMutableSet *infos = [_objectInfosMap objectForKey:object];

  // check for info existence
  _FBKVOInfo *existingInfo = [infos member:info];
  if (nil != existingInfo) {
    NSLog(@"observation info already exists %@", existingInfo);

    // unlock and return
    OSSpinLockUnlock(&_lock);
    return;
  }

  // lazilly create set of infos
  if (nil == infos) {
    infos = [NSMutableSet set];
    [_objectInfosMap setObject:infos forKey:object];
  }

  // add info and oberve
  [infos addObject:info];

  // unlock prior to callout
  OSSpinLockUnlock(&_lock);

  [[_FBKVOSharedController sharedController] observe:object info:info];
}

該方法首先檢查一下之前是否有重複監聽的屬性,如果有的話,直接返回,否則就呼叫_FBKVOSharedController中的監聽方法,之前說過,_FBKVOSharedController是真正實現監聽的內部類。

3 . _FBKVOSharedController內部類

FBKVO實現程式碼使用了很多C,C++的語法。至此開始真正進入observe的具體實現。

- (void)observe:(id)object info:(_FBKVOInfo *)info
{
  if (nil == info) {
    return;
  }

  // register info
  OSSpinLockLock(&_lock);
  [_infos addObject:info];
  OSSpinLockUnlock(&_lock);

  // add observer
  [object addObserver:self forKeyPath:info->_keyPath options:info->_options context:(void *)info];
}

該方法前面都是對引數的記錄,最後一段程式碼是關鍵:

[object addObserver:self forKeyPath:info->_keyPath options:info->_options context:(void *)info];

這段程式碼中,object是之前被監聽的_model類,self是 _FBKVOSharedController,在從頭理一遍程式碼,我們的初衷是要監聽 _model類中的屬性,在這裡變成監聽 _FBKVOSharedController中的屬性。而且是呼叫的系統方法:

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

_FBKVOSharedController內部呼叫上面系統提供的方法,然後重寫了另一個系統方法:

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

通過呼叫API1,對屬性進行監聽,如果屬性出現變化的時候,系統會去呼叫API2,重寫API2,可以控制KVO的具體實現,不論是增加block還是action都可以在API2中進行。

下面是API2的具體實現:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
  NSAssert(context, @"missing context keyPath:%@ object:%@ change:%@", keyPath, object, change);

  _FBKVOInfo *info;

  {
    // lookup context in registered infos, taking out a strong reference only if it exists
    OSSpinLockLock(&_lock);
    info = [_infos member:(__bridge id)context];
    OSSpinLockUnlock(&_lock);
  }

  if (nil != info) {

    // take strong reference to controller
    FBKVOController *controller = info->_controller;
    if (nil != controller) {

      // take strong reference to observer
      id observer = controller.observer;
      if (nil != observer) {

        // dispatch custom block or action, fall back to default action
        if (info->_block) {
          info->_block(observer, object, change);
        } else if (info->_action) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
          [observer performSelector:info->_action withObject:change withObject:object];
#pragma clang diagnostic pop
        } else {
          [observer observeValueForKeyPath:keyPath ofObject:object change:change context:info->_context];
        }
      }
    }
  }
}

方法會檢測,是否要執行block,如果block引數不為空則執行:info->_block(observer, object, change); 如果有action,則用performSelector:執行action,否則呼叫系統自帶的監聽方法。

如果有任何問題歡迎再下面留言,或者掃描二維碼
wechat