it-swarm.com.ru

Должны ли angular $ watch быть удалены при разрушении области?

В настоящее время работаем над проектом, в котором мы обнаружили огромные утечки памяти, когда не очищали подписки на широковещание от разрушенных областей. Следующий код исправил это:

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();
});
52
Andrew

Нет, вам не нужно удалять $$watchers, так как они будут эффективно удалены после уничтожения области действия.

Исходя из исходного кода Angular (v1.2.21), метод Scope's $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 за указывая на это )

Кроме того, убедитесь, что вы отменили регистрацию прослушивателей событий от элементов, которые переживают разрушаемую область. Например. если у вас есть директива, регистрирующая прослушиватель на родительском узле или на <body>, вы также должны отменить их регистрацию.
Опять же, вам не нужно удалять слушателя, зарегистрированного для уничтожаемого элемента.


Вроде не связано с исходным вопросом, но теперь также есть событие $destroyed, отправляемое на уничтожаемый элемент, так что вы также можете подключиться к нему (если это подходит для вашего варианта использования):

link: function postLink(scope, elem) {
  doStuff();
  elem.on('$destroy', cleanUp);
}
84
gkalpak

Я тоже хотел бы добавить @ gkalpak ответ, так как он ведет меня в правильном направлении ..

Приложение, над которым я работал, вызвало утечку памяти, заменив директивы, у которых были часы. Директивы были заменены с использованием jQuery, а затем соблюдались.

Чтобы исправить я добавил следующую функцию ссылки

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

он использует событие уничтожения элемента, чтобы в свою очередь уничтожить область видимости.

2
Kieran