現在、破壊されたスコープからブロードキャストサブスクリプションをクリアしないと、巨大なメモリリークが見つかったプロジェクトに取り組んでいます。次のコードでこれを修正しました。
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();
});
いいえ、$$watchers
を削除する必要はありません。スコープが破棄されると効果的に削除されるためです。
Angularのソースコード(v1.2.21)から、Scope
の$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;
};
}
したがって、$$watchers
の登録を「手動で」解除しても意味がありません。
ただし、イベントリスナーの登録を解除する必要があります(投稿で正しく言及しているように)。
注:他のスコープに登録されているリスナーの登録を解除するだけです。破棄されるスコープに登録されているリスナーを登録解除する必要はありません。
例えば。:
// You MUST unregister these
$rootScope.$on(...);
$scope.$parent.$on(...);
// You DON'T HAVE to unregister this
$scope.$on(...)
( それを指す )の@JohnへのThx
また、破棄されるスコープよりも長生きする要素からイベントリスナーを登録解除するようにしてください。例えば。親ノードまたは<body>
にリスナーを登録するディレクティブがある場合は、それらも登録解除する必要があります。
繰り返しますが、破棄される要素に登録されているリスナーを削除する必要はありません。
元の質問とは無関係ですが、破棄される要素に$destroyed
イベントもディスパッチされるので、それをフックすることもできます(ユースケースに適している場合)。
link: function postLink(scope, elem) {
doStuff();
elem.on('$destroy', cleanUp);
}
私も追加したいと思います @ gkalpak の答えが正しい方向に私を導くので..
私が取り組んでいたアプリケーションは、ウォッチを持っているディレクティブを置き換えることでメモリリークを作成しました。ディレクティブはjQueryを使用して置き換えられ、その後コンパイルされました。
修正するには、次のリンク機能を追加しました
link: function (scope, elem, attrs) {
elem.on('$destroy', function () {
scope.$destroy();
});
}
要素のdestroyイベントを使用して、スコープを順番に破棄します。