[Chrome源码阅读] 理解ObserverList类的实现技巧

来源:互联网 发布:windows 补丁清理软件 编辑:程序博客网 时间:2024/05/16 00:45

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。

原创粉丝点击