当范围被破坏时,是否应该删除angular $ watch?

时间:2014-08-04 07:44:26

标签: javascript angularjs angularjs-directive angularjs-scope

目前正在开展一个项目,当我们发现大量内存泄漏时,不清除已销毁范围内的广播订阅。以下代码修复了此问题:

var onFooEventBroadcast = $rootScope.$on('fooEvent', doSomething);

scope.$on('$destroy', function() {
    //remove the broadcast subscription when scope is destroyed
    onFooEventBroadcast();
});

这种做法是否也可用于手表?以下代码示例:

var onFooChanged = scope.$watch('foo', doSomething);

scope.$on('$destroy', function() {
    //stop watching when scope is destroyed
    onFooChanged();
});

2 个答案:

答案 0 :(得分:84)

不,您不需要删除$$watchers,因为一旦范围被销毁,它们就会被有效删除。

来自Angular的源代码(v1.2.21),Scope的{​​{1}}方法:

$destroy

因此,$destroy: function() { ... if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling; if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling; if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling; if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling; ... this.$$watchers = this.$$asyncQueue = this.$$postDigestQueue = []; ... 数组被清空(范围从范围层次结构中删除)。

从数组中删除$$watchers是取消注册函数的所有功能:

watcher

因此,手动取消注册$watch: function(watchExp, listener, objectEquality) { ... return function deregisterWatch() { arrayRemove(array, watcher); lastDirtyWatch = null; }; } “毫无意义。”


你仍然应该取消注册事件监听器(正如你在帖子中正确提到的那样)!

注意: 您只需要取消注册在其他范围上注册的侦听器。无需取消注册正在销毁的范围上注册的侦听器 E.g:

$$watchers

(Thx to @John for pointing it out

此外,请确保从与被销毁范围相比的元素中取消注册任何事件侦听器。例如。如果你有一个指令在父节点或// You MUST unregister these $rootScope.$on(...); $scope.$parent.$on(...); // You DON'T HAVE to unregister this $scope.$on(...) 上注册一个监听器,那么你也必须取消注册它们。
同样,您不必删除在要销毁的元素上注册的侦听器。


与原始问题无关,但现在还有一个<body>事件在被销毁的元素上调度,所以你也可以挂钩(如果它适合你的用例):

$destroyed

答案 1 :(得分:2)

我想添加@gkalpak的答案,因为它引导我走向正确的方向。

我正在处理的应用程序通过替换拥有手表的指令来创建内存泄漏。使用jQuery替换指令然后编译。

要修复我添加了以下链接功能

link: function (scope, elem, attrs) {
    elem.on('$destroy', function () {
        scope.$destroy();
    });
}

它使用元素destroy事件来反过来破坏范围。