问题描述:

In the router I do this

 function test() {

self.topbarView = new TopbarView();

self.topbarView.render();

GhostviewHunter.addView(self.topbarView);

}

function clean() {

console.log(GhostviewHunter.currentViews.length);

GhostviewHunter.clean();

}

setInterval(test, 1000);

setInterval(clean, 1000);

ghostviewhunter should clean/remove the views:

define('ghostviewHunter', [], function() {

var GhostviewHunter = function() {};

GhostviewHunter.prototype.currentViews = [];

GhostviewHunter.prototype.addView = function(view) {

this.currentViews.push(view);

}

GhostviewHunter.prototype.clean = function() {

_.each(this.currentViews, function(view) {

view.remove();

});

this.currentViews.length = 0;

}

GhostviewHunter.__instance = null;

GhostviewHunter.getInstance = function() {

if( GhostviewHunter.__instance == null ) {

GhostviewHunter.__instance = new GhostviewHunter();

}

return GhostviewHunter.__instance;

}

return GhostviewHunter.getInstance();

})

TopView is fetching a model, the model is updated every 1seconde with setInterval function.

I thought that remove(); would be enough be the memory leak is very quick when I monitor the app.

Any idea ?

EDIT:

TOPBARVIEW

define('topbarView', [

'backbone',

'parameterManager',

'text!views/topbarView/topbarTemplate.html',

'drupalidModel',

'weatherModel',

'refreshTime',

'dateParser'

], function(Backbone, ParameterManager, TopbarTemplate, DrupalidModel, WeatherModel, RefreshTime, DateParser) {

var TopbarView = Backbone.View.extend({

el: '#topbar',

template: _.template(TopbarTemplate),

events: {},

initialize: function() {

var self = this;

_.bindAll(this, 'render', 'startDateRefresh');

this.dateParser = new DateParser();

self.startDateRefresh();

setInterval(self.startDateRefresh, RefreshTime.date);

this.initWeatherModel();

},

render: function() {

var self = this;

var data = {

picto_url : ParameterManager.get('WEATHER_RESOURCE_URL') + ParameterManager.get('WEATHER_PICTO_CODE') + ".png",

date: self.date

}

this.$el.html(this.template({data: data}));

},

initWeatherModel: function() {

var self = this;

var weather_url = ParameterManager.get('WEATHER_URL');

if(weather_url === null) {

this.drupalidModel = new DrupalidModel();

this.drupalidModel.fetch({

success: function(model, response) {

var center_id_num = model.get('center_id_num');

ParameterManager.set('DRUPAL_CENTER_ID_NUM', center_id_num);

ParameterManager.constructWeatherUrl();

self.model = new WeatherModel();

self.listenTo(self.model,'change', self.render);

self.startModelRefresh();

},

error: function() {

console.log("Failed to fetch center id!");

}

})

} else {

this.model = new WeatherModel();

self.listenTo(self.model,'change', self.render);

this.startModelRefresh();

};

},

startModelRefresh: function() {

var self = this;

this.modelRefresh = function() {

self.model.fetch();

}.bind(this);

self.modelRefresh();

setInterval(self.modelRefresh, RefreshTime.weather);

},

stopModelRefresh: function() {

var self = this;

clearInterval( self.modelRefresh );

},

startDateRefresh: function() {

var self = this;

this.date = this.dateParser.classicDate();

this.render();

}

});

return TopbarView;

})

网友答案:

As fbynite suggested, your code which is supposed to clear the interval(s) is not correct, you should pass the interval id to clearInterval.

apart from that, you're not calling stopModelRefresh() at all. You should make sure all external references are properly removed before removing the view. For example I've added a destroy method that clears the interval before removing the view:

var TopbarView = Backbone.View.extend({
  el: '#topbar',
  template: _.template(TopbarTemplate),
  events: {},
  initialize: function() {
  },
  render: function() {
  },
  modelRefresh: function() {
    this.model.fetch();
  },
  startModelRefresh: function() {
    this.modelRefresh();
    this.intervalId = setInterval(_.bind(this.modelRefresh,this), RefreshTime.weather);
  },
  stopModelRefresh: function() {
    clearInterval(this.intervalId);
  },
  destroy: function() {
    this.stopModelRefresh();
    this.remove();
  }

});

Now your GhostviewHunter should call it instead of directly calling remove:

GhostviewHunter.prototype.clean = function() {
   _.each(this.currentViews, function(view) {
            view.destroy();
   });
   this.currentViews.length = 0;
}

or you can even override the remove method itself to something like:

remove: function(){
  this.stopThisInterval();
  this.stopThatInterval();
  this.cleanUpSomethingElse();
  Backbone.View.prototype.remove.call(this);
}

and have the ghost thingy call remove itself.


Note that you have other interval calling startDateRefresh which you're not even attempting to clear... You should clear all such similarly.

And as a side note, I strongly suggest to stop spamming self = this where it is totally unnecessary for eg:

stopModelRefresh: function() {
        var self = this;
        clearInterval( self.modelRefresh );
 // Why..? Nothing here changes the context?
},

and I also suggest recursively calling modelRefresh once the current fetch succeeds/fails rather than calling it from an interval where you have no guarantee that the previous fetch is complete

相关阅读:
Top