简体   繁体   English

迭代事件处理程序集合时,如何安全地从*回调中删除处理程序?

[英]While iterating over a collection of event handlers, how do you safely remove a handler from *within* a callback?

I'm a little puzzled over something. 我对某事感到有些困惑。 Java's documentation tells us that there is no defined behavior when removing items from a collection while iterating over that collection using an Iterator object and that the only safe way to do so is to use Iterator.remove(). Java的文档告诉我们在使用Iterator对象迭代该集合时从集合中删除项目时没有定义的行为,唯一安全的方法是使用Iterator.remove()。

How then, would you safely remove an event handler from an ArrayList if, in the course of iterating through the list one of the handlers has decided that it's time to remove itself as a listener? 那么,如果在迭代列表的过程中,其中一个处理程序决定将它自己作为监听器删除的话,你会如何安全地从ArrayList中删除一个事件处理程序?

// in public class Dispatcher

public void dispatchEvent(){
    Iterator<IEventHandler> iterator = mHandlers.iterator();
    IEventHandler handler = null;
    while(iterator.hasNext()){
        handler = iterator.next();
        handler.onCallbackEvent();
    }
}

public void insertHandler(IEventHandler h){
    mHandlers.add(h);
}

public void removeHandler(IEventHandler h){
    mHandlers.remove(h);
}

Meanwhile, the handler was instantiated like this... 同时,处理程序像这样被实例化......

final Dispatcher d = new Dispatcher();
d.insertHandler(new IEventHandler(){
    @Override
    public void onCallbackEvent(){
        Log.i(" callback happened ");
        d.removeHandler(this);
    }
});

See the potential problem? 看到潜在的问题? You're removing the handler from the ArrayList as a result of the onCallbackEvent() declared in that specific handler while you're still iterating using the Iterator . 您正在使用Iterator进行迭代时 ,由于在该特定处理程序中声明的onCallbackEvent() 从ArrayList中删除处理程序。

Is this an intractable problem? 这是一个棘手的问题吗? What's the safe way to handle this situation? 处理这种情况的安全方法是什么?

This is a very common problem when implementing an event system. 这是实现事件系统时非常常见的问题。 The only solution is to copy the list of handlers on change. 唯一的解决方案是在更改时复制处理程序列表。 You can do this yourself in the insertHandler/removeHandler methods or just use CopyOnWriteArrayList. 您可以在insertHandler / removeHandler方法中自己执行此操作,或者只使用CopyOnWriteArrayList。

You could reimplement removeHandler to store the handlers scheduled for removal. 您可以重新实现removeHandler来存储计划删除的处理程序。

public void removeHandler(IEventHandler h){
    mHandlersToRemove.add(h);
}

and then remove them before you do any dispatching. 然后在进行任何调度之前将其删除。

public void dispatchEvent(){
    mHandlers.removeAll(mHandlersToRemove);
    mHandlersToRemove.clear();
    ...

You could also remove at the end of dispatchEvent , but then you could only remove from within handlers. 您也可以删除在年底dispatchEvent ,但你只能从处理程序移除。 (Otherwise you might dispatch to handlers that were removed.) (否则您可能会分派给已删除的处理程序。)


If you're interested in a theoretical solution to this problem, you can look at how C++ implements iterators. 如果您对此问题的理论解决方案感兴趣,可以查看C ++如何实现迭代器。 In an stl vector, the iterator has an erase method that returns the next valid iterator. 在stl向量中,迭代器有一个擦除方法 ,它返回下一个有效的迭代器。

It would look something like this: 它看起来像这样:

for (itr = listA.begin(); itr != listA.end(); )
{
    if ( shouldRemove(*itr) ) {
        itr = listA.erase(itr);
    }
    else {
      ++itr;
    }
}

Of course, this example doesn't apply to your question since it's both in C++ and it would be awkward to propagate the new iterator up to the top-level loop (or add a return value to your call for a "remove" condition). 当然,这个例子不适用于你的问题,因为它都是在C ++中,将新迭代器传播到顶级循环(或者为你的调用添加一个返回值以获得“删除”条件)会很尴尬。 But maybe there's a similar java implementation somewhere out there : ) 但也许在某处有一个类似的java实现:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM