如果你正在编写的类会通过回调函数或Observer模式广播某种状态的改变或时间的发生,那么请小心:你无法预料Observer会做什么事情,它可能又会调用这个类的某个方法,从而导致你的类被“重入”,这可能会破坏类状态。下面是本人在写代码时遇到过的类似情形:
void ClassA::DoSomething() { for(int i=0; i<mDataArray.count(); i++) { ProcessData(mDataArray[i]); observer->SomethingHappend(mDataArray[i]); } }
类ClassA::DoSomething()会处理某些数据,同时会将该情况通报给observer(假设关系已经预先设定好)。这个假设observer在SomethingHappened的实现中又调用了ClassA的RemoveData方法,那么不幸的事情发生了。
这种问题有以下几种解决方案:
1.对回调函数或事件处理函数施加一定的约束:比如规定了SomethingHappened的实现就不能call RemoveData方法。在GUI编程中,
在对窗体的sizeChange事件处理中,不允许调整该窗体的size,否则会造成死循环,这个约束是合理的,自然的,容易让人理解的。但事情并不总是如此,如果程序的需求就要求能在SomethingHappened调用RemoveData呢?
2、用post event取代回调函数,既在广播某种情况的发生时,不是通过回调函数,而是post一个event给observer对象。这样就使得DoSomething()的执行不会被打断,避免了类的重入。这种方法一遍需要某种Framework的支持。
3、在Symbian下第二种方法可以用AO来实现,就是规定所有的observer在收到广播后启动一个CTimer AO,在这个AO的RUNL方法里面在去执行业务逻辑。
4、调整DoSomething()代码:先复制一份mDataArray到函数局部,基于局部数据发出广播。