1. 程式人生 > >[Chrome原始碼閱讀] 理解ObserverList類的實現技巧

[Chrome原始碼閱讀] 理解ObserverList類的實現技巧

Chrome中大量用到了Observer模式,比較關鍵的類是ObserverList。

這個類的comment,提到了一個很關鍵的問題,就是在loop每個observer時,可能有observer嘗試著被呼叫RemoveObserver,從列表中去除,而ObserverList內部是借用std::vector儲存所有的observer,這樣就會導致一個問題,就是std::vector::erase函式可能會invalidate所有的iterator,後續的notification就會失敗。

//   A container for a list of observers.  Unlike a normal STL vector or list,
//   this container can be modified during iteration without invalidating the //   iterator.  So, it safely handles the case of an observer removing itself //   or other observers from the list while observers are being notified.
#define FOR_EACH_OBSERVER ( ObserverType, observer_list , func )  \
  do {                                                        \
    ObserverList< ObserverType >::Iterator it( observer_list );   \
    ObserverType* obs ;                                        \
    while (( obs = it . GetNext()) != NULL )                      \
      obs ->func ;                                              \
  } while (0)

Chrome用一個變數輕鬆了就解決了這個問題: int notify_depth_。

當初始化Iterator時,會將ObserverList中的這個變數值增加1,銷燬時時呼叫ObserverList::Compact函式來去除那些observer為空的元素。那些observer為空的(指標為NULL)元素,就是呼叫RemoveObserver函式時設定的。我們總是用NULL來標記那些去除了的observer。

當我們嘗試著呼叫RemoveObserver時,如果notify_depth_不為0, 會將observer對應的元素指標設為0,延遲去除。因為這時正在遍歷列表的元素。

   // Remove an observer from the list.
  void RemoveObserver (ObserverType * obs) {
    typename ListType ::iterator it =
      std ::find ( observers_. begin (), observers_ . end(), obs );
    if ( it != observers_ . end()) {
      if (notify_depth_ ) {
        * it = 0;
      } else {
        observers_ .erase ( it);
      }
    }
  }

當遍歷退出時,就會呼叫ObserverList::Compact函式,嘗試著去除那些已經標記要去除的observer。

   void Compact () const {
    typename ListType ::iterator it = observers_ .begin ();
    while ( it != observers_ . end()) {
      if (*it ) {
        ++ it ;
      } else {
        it = observers_ . erase( it );
      }
    }
  }

還有一個地方要特別注意,如果去除的是正在被通知的observer後面的observer,GetNext函式呼叫到它時就會跳過那個元素。Iterator::GetNext()也做足了功夫:
    ObserverType* GetNext() {
      ListType& observers = list_.observers_;
      // Advance if the current element is null
      size_t max_index = std::min(max_index_, observers.size());
      while (index_ < max_index && !observers[index_])
        ++index_;
      return index_ < max_index ? observers[index_++] : NULL;
    }

這樣我們就能用GetNext函式遍歷到所有有效的observer。