WeakCallback是nbase库的一个重要部分,主要用来检测对象的生命周期。下面结合源码分析他是如何检测生命周期以及其缺陷。
SupportWeakCallback
1 | class BASE_EXPORT SupportWeakCallback |
SupportWeakCallback
类的主要作用是提供被检测生命周期的功能,如果一个类需要被监控(例如UI窗口),则继承这个类即可。检测方法主要通过智能指针shared_ptr
实现。成员变量中有个WeakFlag
类型的shared指针,成员方法将其对应的weak指针暴露出来。这样子,持有weak指针的一方就可以在任何时候通过weak_ptr::expired
方法判断对应shared指针是否存在,从而检测到对象的生命周期。WeakFlag
是一个空类,占位使用。
WeakCallback
1 | template<typename T> |
WeakCallback
可以将一个对象跟一个weak指针绑定在一起,在实际应用中主要配合可执行对象使用。它实现了operator ()
函数,当weak指针所绑定的对象存在时,才执行函数。
Bind
1 | // global function |
继承了SupportWeakCallback
的类,应该使用nbase::Bind
而非std::Bind
。因为nbase::Bind
能生成WeakCallback
的可执行对象,比std::Bind
多了一个生命周期的检测,更加安全。对于全局函数nbase::Bind
退化成std::Bind
WeakCallbackFlag
1 | class BASE_EXPORT WeakCallbackFlag final : public SupportWeakCallback |
SupportWeakCallback
一般用于类的继承,而WeakCallbackFlag
一般用在成员变量。SupportWeakCallback
只有在析构的时候才会销毁对应的shared指针,但WeakCallbackFlag
可以通过主动调用Cancel销毁,比SupportWeakCallback
更轻便。在实际应用中主要配合nbase::ThreadManager::PostRepeatedTask
当定时器使用
缺点
WeakCallback
的优点在于,它提供一种比较优雅的方式去检测对象的生命周期;但是缺点在于,它并不是线程安全的。比较典型的例子是这样的
1 | // 继承了SupportWeakCallback类的成员函数 |
当初我在项目里很容易就把ToWeakCallback
顺手写进去了,但是不对的。问题就在第二个ToWeakCallback
里。它是在Http线程里被调用,并且没有锁操作。因此有可能在执行第一个ToWeakCallback
的时候this是存在的,但是在生成第二个ToWeakCallback
的时候已经销毁。正确的写法是这样的
1 | // 继承了SupportWeakCallback类的成员函数 |
在http线程不操作this,而是传递weak指针,留到UI线程再去判断。