Подтвердить что ты не робот

Должны ли быть удалены часы angular $, когда область уничтожена?

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

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();
});
4b9b3361

Ответ 1

Нет, вам не нужно удалять $$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(...)

(спасибо to @John для указав это)

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


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

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

Ответ 2

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

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

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

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

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