[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。
- [Chrome源码阅读] 理解ObserverList类的实现技巧
- [Chrome源码阅读] 理解Thread类实现
- [Chrome源码阅读] 理解Chrome的smart pointer
- [Chrome源码阅读] 理解Chrome的smart pointer
- [Chrome源码阅读] MessageLoop类的点点滴滴
- [Chrome源码阅读] Browser相关的类
- [Chrome源码阅读] Browser相关的类
- [Chrome源码阅读] 理解Browser进程
- [Chrome源码阅读] 理解Browser进程
- [Chrome源码阅读] 理解Chrome导航网址的流程及render进程启动模式
- 阅读源码的小技巧
- [Chrome源码阅读] Chrome的多进程调试方法
- 关于Intellij阅读源码的一些技巧
- [Chrome源码阅读]2个重要的类RenderViewHost和TabStripModel
- [Lua源码阅读] 理解Lua的object
- IntentService的理解及源码阅读
- ArrayList源码的阅读与理解
- 源码阅读小技巧
- Android界面开发之拖动条及自定义SeekBar
- yum proxy
- Dynamics CRM 2011 编程系列(3):JS脚本编程之onsave事件
- UnsupportedClassVersionError的解决
- DataGridView进行添加、修改、删除数据操作-C#
- [Chrome源码阅读] 理解ObserverList类的实现技巧
- 最短路径算法整理
- JBoss AS7 域、部署项目和域名绑定
- HowTomcatWork 笔记 1 Servlet 容器做的3件事情
- Oracle的体系结构(一)概述
- Android自动测试平台工具开发
- Struts简易计算器:页面
- 京东商城的“地下世界”
- Dynamics CRM 2011 集成Sharepoint server 2010 报错“由于Internet Explorer安全设置的原因,无法确定SharePoint的版本”