Ionic(或angular)中的内存泄漏?

Memory leak in Ionic(or angular)?

我是 Ionic 和 Angular 的新手,所以我不确定问题实际上出在两者之间(如果有的话)。我似乎对 ng-repeat 不清除内存有疑问。我写了一个简单的例子来演示

http://codepen.io/pen/pvQyxj

index.html

<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
    <title></title>

    <link href="//code.ionicframework.com/nightly/css/ionic.css" rel="stylesheet">

    <!-- ionic/angularjs js -->
    <script src="//code.ionicframework.com/nightly/js/ionic.bundle.js"></script>
    <script src="cordova.js"></script>
    <script src="js/app.js"></script>

  </head>
  <body ng-app="starter">

    <ion-pane>
      <ion-header-bar class="bar-stable">
        <h1 class="title">Ionic Blank Starter</h1>
      </ion-header-bar>
      <ion-content ng-controller="CardsCtrl">
    <div ng-repeat="card in cards" on-tap="start(1000)">
        <img ng-src="{{card.image}}">
    </div>
      </ion-content>
    </ion-pane>
  </body>
</html>

js/app.js

angular.module('starter', ['ionic'])

.run(function($ionicPlatform) {
  $ionicPlatform.ready(function() {
    // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
    // for form inputs)
    if(window.cordova && window.cordova.plugins.Keyboard) {
      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
    }
    if(window.StatusBar) {
      StatusBar.styleDefault();
    }
  });

}).controller('CardsCtrl', function($scope) {
    $scope.cards = [];


    var addCards = function(_num, y) {
        for (var x = 0; x < _num; x++) 
        {
            $scope.cards.push({
                image:"http://i.imgur.com/QR8mZAt.jpg?"+y
            }); 
        }
    }
    addCards(1, "new");

    var cardDestroyed = function(y) {

        //console.log("destroy");
        $scope.cards.splice(0, 1);
        addCards(1, y);
    };

    $scope.start = function(y){
        console.log("start");
        for (var x = 0; x < y; x++)
        {
            cardDestroyed(y);
        }
        console.log("stop");
    }
})

每次点击图片,ng-repeat 使用的数组都会删除并添加一个元素 1000 次。但是内存中有东西残留,导致我的实际应用程序崩溃。

任何人都可以告诉我我做错了什么,或者它是否真的是一个错误?我查看了 github,但没有看到与我所看到的相符的错误报告,一旦我确定这不仅仅是我做错了什么,我就会去报告。

如果您按照图表进行操作,您会发现问题出在发出的事件侦听器的数量上。由于 $watch 在 angular 中的工作方式,从数组中删除元素然后添加新元素不会删除旧元素上的 $watch,从而导致事件数听众继续无限产生。

默认情况下,$watch() 函数只检查对象引用是否相等。这意味着在每个 $digest 中,angular 将检查新旧值是否是相同的 "physical" 对象。这意味着 vanilla $watch() 语句只会在您实际更改基础对象引用时调用其处理程序。

有两种方法可以解决这个问题。首先,您可以使用 Angular 1.3 中提供的新的 Bind Once 语法。将 ng-repeat 更改为 ng-repeat="card in ::cards" 将仅在计算表达式之前创建绑定,然后销毁侦听器。这非常适用于您知道元素一旦被计算就永远不会改变的情况。

另一种方法是对元素使用更积极的跟踪。 ng-repeat="card in cards track by $id"会导致元素被$id字段跟踪,而angular会在具有唯一性$id的对象不存在时智能移除监听器DOM.

在时间轴上观察这两个,您会看到第一个选项 Bind Once 将花费更多时间没有听众,因为它会在评估后销毁听众并且只产生新的听众以将新元素添加到DOM。第二个选项将大部分时间花在侦听器的上限上,以获得活动元素的数量,但会在元素被删除时删除所有侦听器并添加新的侦听器来替换它们。

最后,如果您知道元素不会改变,只会被替换,那么 Bind Once 会更有效率;如果您的元素可以在添加和删除之间变化,跟踪将更加灵活。