[Chrome原始碼閱讀] 理解ObserverList類的實現技巧
阿新 • • 發佈:2019-01-31
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,#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。