我正在尝试找出使用匿名事件侦听器的最佳选择,这些侦听器在使用后会100%收集垃圾。我得到了这两个例子,但我想知道它们之间是否真的有任何区别......
var listener1:Function = function(e:Event):void
{
resource.removeEventListener(e.type, listener1);
loadedHandler(resource);
listener1 = null;
};
resource.addEventListener(ResourceEvent.LOADED, listener1);
或者这个......
resource.addEventListener(ResourceEvent.LOADED, function(e:Event):void
{
Resource(e.currentTarget).removeEventListener( e.type, arguments["callee"]);
loadedHandler(resource);
}, false, 0, true);
或者还有其他更好的解决方案吗?对我来说非常重要的是,这些侦听器和函数可以从内存中正确删除,因为它们可能在应用程序中经常执行。我可以去使用一个字典来映射所有的监听器等,然后在非匿名监听器等等中测试和删除它们,但是这会很快得到很快的参与,因为可能存在资源可以同时异步加载的情况在应用程序的不同类中。
@dominic: 你的意思是将这样的函数放入方法体中,对吧?正如我在上面写的那样,应用程序异步加载资源,并且可能发生当前正在加载资源,而来自应用程序中其他位置的另一个类请求相同的资源。资源管理类(其中所述代码所在的)然后连接到由资源调度的事件侦听器。据我所知,如果我使用类示例或函数作为侦听器,它们将被新请求重用,旧请求的事件将永远不会触发。因此匿名函数存储在变量中。我假设他们都留在记忆中,直到他们各自的要求完成。 但也许我完全混淆了这种情况并非如此?我有时发现事件很难掌握。
答案 0 :(得分:2)
第一个看起来更干净,您可以避免使用强制转换和数组查找来删除侦听器。 你为什么使用“匿名”处理函数? 你可以像这样使用它,它有点干净,这就是asdocs建议使用事件模型的方式。
function listener1(e:ResourceEvent):void
{
resource.removeEventListener(e.type, listener1);
loadedHandler(resource);
};
resource.addEventListener(ResourceEvent.LOADED, listener1);
答案 1 :(得分:2)
这不直接回答你的“vs”问题,但我即将发布一个名为Reactive Extensions的.NET raix框架的AS3端口,它解决了这样的问题:
Observable.fromEvent(resource, ResourceEvent.LOADED)
.take(1)
.subscribe(function(e : ResourceEvent) : void
{
// just the subscriber code
});
take的使用意味着它会在第一个事件后自动取消订阅。如果您不喜欢匿名函数,也可以手动订阅对方法的引用,而不必担心以后删除它。
虽然不打算这样做,但如果这个答案过于“垃圾”,请告诉我,我会将其删除。
答案 2 :(得分:1)
事件处理函数/方法在这里确实不是问题。
如果我理解你的意思,那就是你有一个ResourceManager类,当你的应用中的一个类请求时,它会加载某个资源。资源完全加载后,资源将调度将触发事件处理程序的事件:
resource.addEventListener(ResourceEvent.LOADED, listener1);
现在将调用listener1函数或方法。 问题是,如果在listener1中你决定删除事件监听器,现在进一步加载资源的完成将触发listener1处理程序,因为管理器不再监听它。 如果应用程序中的两个不同的类同时加载资源,则每个ResourceEvent.LOADED事件将调用一次listener1处理程序。
在我看来,你应该离开事件监听器,只有在加载了所有资源后才删除它,并使用管理器来控制对资源加载的访问,这样它就是集中的,所有的ResourceEvent.LOADED事件都会由listener1函数/方法处理。 然后是cource,如果您的应用程序在其整个生命周期内加载资源,请不要删除该侦听器,只在您不再需要它时将其删除一次。
我不是百分百肯定我理解你的意思所以我希望我不是这里的主题! 希望这会有所帮助。
答案 3 :(得分:0)
我认为在这种情况下使用匿名侦听器没有任何优势(但在某些其他情况下可能更清晰,但我认为你在这里没有任何收获)。
无论如何,有几点需要注意:
1)你的第二个选择是要求麻烦。不要对匿名函数使用弱引用。可以在调度事件之前收集您的侦听器。 (我说不要使用弱引用期,但在大多数情况下是个人品味;但在这种情况下,它不是。)
2)只要您可以确定侦听器将始终触发并且这是将被分派的唯一事件,此方法将起作用。如果您的调度员发出一些事件来发出错误信号,那么您的听众将不会被删除。
3)删除侦听器不一定与垃圾回收有关。在某些情况下,不删除侦听器将导致泄漏(例如,在监听舞台时)。但是,在大多数情况下,不删除侦听器不会导致泄漏(即使使用强引用)。我知道你可能想删除监听器,因为你不再想听一些事件,这是正确的,但我只是添加了这一点。
答案 4 :(得分:0)
感谢所有提示!我现在通过创建一个简单的映射来使用稍微更复杂的解决方案,即加载和失败处理程序由资源ID映射。这是基本的想法:
这取代了原来的代码:
private var _listeners:Object = {};
public function load(resourceID:String):void
{
...
if (loadedHandler != null || failedHandler != null)
{
_listeners[resource.id] = new ListenerVO(loadedHandler, failedHandler);
if (loadedHandler != null)
{
resource.addEventListener(ResourceEvent.LOADED, onResourceProcessed);
}
if (failedHandler != null)
{
resource.addEventListener(ResourceEvent.FAILED, onResourceProcessed);
}
}
...
}
这是新的事件处理程序:
private function onResourceProcessed(e:ResourceEvent):void
{
var r:Resource = e.resource;
var handler:Function;
r.removeEventListener(e.type, onResourceProcessed);
if (e.type == ResourceEvent.LOADED)
handler = ListenerVO(_listeners[r.id]).loadedHandler;
else if (e.type == ResourceEvent.FAILED)
handler = ListenerVO(_listeners[r.id]).failedHandler;
if (handler != null)
{
_listeners[r.id] = null;
delete _listeners[r.id];
handler(r);
}
}
一个小型的包级VO,用于保持所有强类型......
class ListenerVO
{
public var loadedHandler:Function;
public var failedHandler:Function;
public function ListenerVO(lh:Function, fh:Function)
{
loadedHandler = lh;
failedHandler = fh;
}
}
让我知道你对这个想法的看法!