目前正在开展一个项目,当我们发现大量内存泄漏时,不清除已销毁范围内的广播订阅。以下代码修复了此问题:
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();
});
答案 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事件来反过来破坏范围。