Должен ли $watch в Angular удаляться при уничтожении scope?
Описание проблемы
Работаю над проектом, в котором мы обнаружили серьезные утечки памяти из-за того, что не очищали подписки на широковещательные сообщения в уничтоженных scopes. Мы исправили эту проблему следующим образом:
var onFooEventBroadcast = $rootScope.$on('fooEvent', doSomething);
scope.$on('$destroy', function() {
// Удаляем подписку на широковещательное сообщение при уничтожении scope
onFooEventBroadcast();
});
Вопрос: следует ли применять такую же практику для watches
? Пример кода ниже:
var onFooChanged = scope.$watch('foo', doSomething);
scope.$on('$destroy', function() {
// Останавливаем отслеживание при уничтожении scope
onFooChanged();
});
Нужна ваша помощь: правильно ли я понимаю, что необходимо очищать watches
аналогичным образом для предотвращения утечек памяти?
1 ответ(ов)
Нет, вам не нужно вручную удалять $$watchers
, так как они будут эффективно удалены, как только область (scope) будет уничтожена.
Из исходного кода Angular (v1.2.21), метод $destroy
для Scope
выглядит следующим образом:
$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
.
Однако вам все же следует отменять регистрацию слушателей событий, как вы правильно упомянули в своем посте!
Замечание: Вам нужно отменять регистрацию слушателей, зарегистрированных на других областях. Нет необходимости отменять регистрацию слушателей, зарегистрированных на уничтожаемой области.
Например:
// Вы ДОЛЖНЫ отменить регистрацию этих слушателей
$rootScope.$on(...);
$scope.$parent.$on(...);
// ВЫ НЕ ДОЛЖНЫ отменять регистрацию этого слушателя
$scope.$on(...)
Также убедитесь, что вы отменяете регистрацию любых слушателей событий из элементов, которые переживают уничтожаемую область. Например, если у вас есть директива, которая регистрирует слушатель на родительском узле или на <body>
, то вы должны также отменить их регистрацию.
Несколько не связано с оригинальным вопросом, но теперь также существует событие $destroyed
, которое вызывается на элементе, который уничтожается, так что вы можете использовать это событие, если это подходит для вашего случая:
link: function postLink(scope, elem) {
doStuff();
elem.on('$destroy', cleanUp);
}
AngularJS - Условное использование атрибутного директивы
Работа с $scope.$emit и $scope.$on в AngularJS
Как привязать значения списка чекбоксов в AngularJS?
Как установить свойство value в ng-options AngularJS?
AngularJS - UI Router - Программное добавление состояний