离子(或角度)中的内存泄漏? [英] Memory leak in Ionic(or angular)?

查看:23
本文介绍了离子(或角度)中的内存泄漏?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对 Ionic 和 Angular 都不熟悉,所以我不确定这两者之间的实际问题在哪里(如果有的话).我似乎对 ng-repeat 不清除内存有问题.我写了一个简单的例子来演示

http://codepen.io/pen/pvQyxj

index.html

<头><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><body ng-app="starter"><离子窗格><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 card" on-tap="start(1000)"><img ng-src="{{card.image}}">

</离子含量></ion-pane></html>

js/app.js

angular.module('starter', ['ionic']).run(function($ionicPlatform) {$ionicPlatform.ready(function() {//默认隐藏辅助栏(删除此项以显示键盘上方的辅助栏//用于表单输入)如果(window.cordova && window.cordova.plugins.Keyboard){cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);}如果(窗口.状态栏){StatusBar.styleDefault();}});}).controller('CardsCtrl', function($scope) {$scope.cards = [];var addCards = function(_num, y) {for (var x = 0; x <_num; x++){$scope.cards.push({图片:"http://i.imgur.com/QR8mZAt.jpg?"+y});}}addCards(1, "new");var cardDestroyed = function(y) {//console.log("销毁");$scope.cards.splice(0, 1);添加卡片(1,y);};$scope.start = function(y){console.log("开始");for (var x = 0; x 

每次单击图片时,ng-repeat 使用的数组都会删除和添加 1000 次元素.但是内存中存在一些问题,导致我的实际应用崩溃.

任何人都可以了解我是否做错了什么,或者它实际上是一个错误吗?我浏览了 github 并没有看到与我所看到的相符的错误报告,一旦我确定这不仅仅是我做错了什么,我就会去报告.

解决方案

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

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

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

另一种方法是对元素使用更积极的跟踪.ng-repeat="card in card track by $id" 将导致元素被 $id 字段跟踪,并且 angular 将能够巧妙地删除侦听器当具有唯一 $id 的对象不再存在于 DOM 中时.

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

最后,如果你知道元素不会改变,只会被替换,那么绑定一次会更有效率;如果您的元素可以在添加和删除之间发生变化,跟踪将更加灵活.

I'm new to both Ionic and Angular, so I'm not sure where the problem would actually lie between the two(if either). I seem to have a problem with ng-repeat not clearing memory. I've written a bare bones example to demonstrate

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");
    }
})

every time you click the picture, the array ng-repeat is using will have an element removed and added 1000 times. But there is something sticking around in memory, causing my actual app to crash.

Can anyone shed some insight as to if I'm doing something wrong, or if it's actually a bug? I've looked through github and haven't seen a bug report which matches what I'm seeing, and once I've determined it's not just me doing something wrong I'll go make a report.

解决方案

If you follow the chart, you can see that the issue is the number of Event Listeners that are issued. Due to the way that $watch works within angular, removing an element from an array and then adding a new element doesn't remove the $watch on the old element, causing the number of Event listeners to continue to spawn infinitely.

By default, the $watch() function only checks object reference equality. This means that within each $digest, angular will check to see if the new and old values are the same "physical" object. This means that the vanilla $watch() statement will only invoke its handler if you actually change the underlying object reference.

There are two ways to solve this issue. Firstly, you can use the new Bind Once syntax that is provided in Angular 1.3. Changing your ng-repeat to ng-repeat="card in ::cards" will create a bind only until the expression is evaluated, then destroy the listener. This is ideal for situations where you know that the element will never change once it's been evaluated.

The other method is to use a more aggressive tracking on the elements. ng-repeat="card in cards track by $id" will cause the elements to be tracked by the $id field, and angular will be able to smartly remove the listener when the object with the unique $id no longer exists in the DOM.

Watching both of these in the timeline, you will see that the first option Bind Once will spend more time with no listeners, as it will destroy the listeners after evaluation and only spawn new listeners to add the new elements to the DOM. The second option will spend most of it's time at the upper bounds of listeners for your number of active elements, but will drop all the listeners as elements are removed and add new listeners to replace them.

In the end, Bind Once will be more efficient if you know the elements aren't going to change, only be replaced; Tracking will be more flexible if your elements could change between being added and removed.

这篇关于离子(或角度)中的内存泄漏?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆