事件监听方法“once”的两个疑问

操作系统:Windows 7 X64 旗舰版
Cocos Creator版本: 1.6.2 beta2
测试环境:模拟器

项目中用到了once来监听一些事件,但是在使用途中发现两个问题

1.使用once监听的事件会被重复监听,也就是说,事件类型、事件回调、target都一致的情况下,事件会被重复监听,比如我调用以下代码:

        for (var i = 0; i < 10; i++) { 
            this.node.once('aaa', this.test);
        }

那么“aaa”这个事件会被监听10次,触发的时候也会触发10次回调函数,所以想问问是设计如此吗?还是bug?如果是设计如此,那么原因是什么呢?

2.使用once监听事件会导致内存泄漏,我调用以下代码:

        for (var i = 0; i < 100000; i++) { 
            this.node.once('aaa', this.test);
        }

创建了100000个监听器,监听的时候可以看到内存明显增长,虽然说重复创建了那么多监听器,但是按理说如果事件被触发后,监听器会被关闭,内存应该就会被释放啊,但是事实是,监听器被触发后,内存不会下降。

如果用on监听则不会出现重复监听和内存泄漏的现象,但是我看了源码,once也是调用的on来监听,只不过多了一层回调用于关闭事件监听:

我不知道问题出在哪里,所以特来请教一下各位大神,望不吝赐教:joy: @jare @panda

once是在事件函数触发前把它移除了,实现只响应一次事件的功能,不用自己手动移除事件。
还有你说的once内存泄漏是因为, once函数中会动态生成一个局部函数,做为on函数的事件处理函数。

proto.once = function (type, callback, target, useCapture) {
    var self = this;
    //每次都会生成一个cb函数
    var cb = function (event) {
        self.off(type, cb, target, useCapture);
        callback.call(this, event);
    };
    this.on(type, cb, target, useCapture);
};

你调用一100000次就生成了10000个cb函数,他们的地址都不相同。

on = function (type, callback, target, useCapture) {
    
    ...
    //检查是否已经存在type、callbcak、target相同的监听者
    if ( ! listeners.has(type, callback, target) ) {
        listeners.add(type, callback, target);

        if (target && target.__eventTargets)
            target.__eventTargets.push(this);
    }
    return callback;
};

而在on函数中会检查是否已经存在type、callback、target相同的监听者,每次调用once函数cb都是不同的,所以不会被释放。

1赞

原因是 once 没有去重,内部会重新 on 一个匿名方法。匿名方法每次都会创建一个新的,所以就导致底层监听了灰常多。这个问题在 node.js 的 EventEmitter 中也存在,我们之后会修正,谢谢反馈。

这个应该不会才对。应该是因为你在模拟器上,需要调用 cc.sys.gabageCollect() 才会触发 GC。

多谢回复,了解了,但是,这样是不是有问题?就是局部函数不能被释放,这就内存泄漏了啊

刚刚测试了一下,模拟器预览初始内存为大约为47MB,添加100000个监听器后,内存在58MB左右,然后我触发监听器后内存在63MB左右,手动GC后内存还是在57-58MB左右,我试了两次都得到同样的结果

当注册once事件被触发了,会执行off操作,内存就释放了。如果事件没有触发,只能手动移除。不过手动移除参数不够丰富,无法做到移除指定的函数,once注册的事件只能用target参为参数,它会把所关联target的事件给移除了,要慎用。
节点被释放时应该会释放所有事件吧,我没实验过,你可以试试。

我测试了下,这里应该是没有内存泄露的,只不过内存被模拟器保留了,下次避免再重复申请罢了。你可以多试几次,内存峰值会慢慢稳定下来,不会持续上涨。

好的,谢谢:slightly_smiling:

这个问题会在 1.7 修复,谢谢反馈哈
https://github.com/cocos-creator/engine/pull/2031