循环引用内存泄漏?

时间:2014-07-17 09:08:46

标签: javascript backbone.js memory-leaks

我怀疑以下设计模式是否会导致内存泄漏 我已经成功地使用它已经有一段时间了,但是我还没有看到其他人使用过这种模式,所以如果你看到它出了问题,我想要一些确认。
从下个月开始,我必须开始研究一个大型项目,我想知道我可以毫无问题地使用它,或者我应该使用其他策略。

controller.js:

var Controller = function(options){ 
}; 

Controller.prototype.makeView = function(options){ 
    options.controller = this; 
    options.otheroption = options.otheroption; 
    var view = new View(options); 
}; 

Controller.prototype.getModel = function(options){ 
    //--- Get model --- 
    var model = new Model(); 
    var promise = model.fetch(); 
    return promise; 
}); 

view.js:

var View = Backbone.View.extend({ 
    initialize: function(options){
        this.controller = options.controller; 
        this.otheroption = options.otheroption; 
    }, 
    getModel: function(){ 
        var promise = this.controller.getModel(); 
        promise.done(_.bind(function(model){
            //Do something with the returned model instance 
        }, this)); 
    }; 
}); 

实例化控制器,例如。来自路由器或其他控制器:

//--- Instantiate the controller and build the view ---// 
var controller = new Controller(); 
controller.makeView(options)

对我来说,这看起来不像循环引用,因为控制器和视图都被声明为局部变量。 然而,实例化的视图可以访问控制器功能,这允许我通过视图使用的模型/集合来隔离RESTful服务器交互。

对我来说,似乎剩下的唯一引用就是保持对控制器对象的引用的视图。 之后我做的是清理视图(当我不再需要它时,我会销毁实例及其引用。

您对此模式的看法表示高度赞赏 我的目的是在单独的控制器文件中隔离视图/服务器交互的创建:如果您在我的方法中看到漏洞并有更好的方法,请分享。

感谢。

3 个答案:

答案 0 :(得分:3)

在这里做什么控制器看起来像是一个实用工具。可以由全球级单身人士轻松管理。我第一眼就看到了一些问题。

  • 代码重复,假设您要为不同类型的模型和视图创建单独的Controller,则需要为每个控制器重复makeView和getModel代码。如果从BaseController扩展,则需要将View和Model Class传递给getModel和makeView函数。
  • 如何处理必须在不同视图中使用相同模型的用例?
  • makeView和getModel的设计假设每个makeView都会有一个getModel调用,按假设顺序

我宁愿编写一个可以为我创建和部署视图的实用程序函数。

 var deployView = function(view, config){
     //do the view rendering
     view.render();
     view.$el.appendTo(config.el);
 }
 var createView  = function(config) {

     var view;
     var viewType = 'model';

     if (config.collection || config.Collection) {
         viewType = 'collection';
     }

     if (viewType === 'model') {
         if (config.Model) {
             config.model = new config.Model(config.modelAttributes);
             //fetch if needed
         }
     } else {
         if (config.Collection) {
             config.collection = new config.Collection(config.items);
             //fetch if needed
         }
     }

     var filteredConfig = _.omit(config, 'Collection', 'Model', 'View');
     view = new config.View(filteredConfig);
     deployView(view, filteredConfig)
 }

答案 1 :(得分:3)

简短回答:您发布的代码中没有内存泄漏问题。视图包含对控制器的引用,但反之亦然。因此,只要控制器的寿命比视图长,该引用就不会使您的对象不被垃圾收集。我在代码中的任何地方都没有看到循环引用。

更长的答案:陷阱将出现在发布的代码中。特别是,您的视图中的任何事件处理程序都必须正确清理,否则您的视图never fade into oblivion。但是你已经在你的问题中说你清理了你的观点,所以我猜你知道那种问题。

答案 2 :(得分:1)

JavaScript实现很长一段时间没有圆形引用的问题。 (如果我没记错的话,IE6确实有来自循环引用的内存泄漏,这段时间内任何其他主要浏览器都没有共享。)

现代JavaScript实现通过“标记和扫描”算法执行垃圾收集。首先,他们从全局对象开始扫描您的Web应用程序的整个内存结构,并标记他们找到的所有内容。然后他们扫描存储在内存中的每个对象,垃圾收集任何未标记的内容。只要没有从全局对象或任何存储函数引用您的对象,就可以对其进行垃圾回收。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management#Mark-and-sweep_algorithm

您可能正在考虑基于引用计数的实现,它确实存在循环引用中的内存泄漏问题。在该实现中,只要一个对象包含对另一个对象的引用,该第二个对象就不能被垃圾回收。该方法曾经在Web浏览器中使用过,但现在已经不再使用了。

如今,大多数内存泄漏来自全局可访问的对象,您忘记清理并意外地将数据保留在函数闭包中(一个创建另一个函数并将其传递/保存到某处的函数)。由于闭包的局部变量可以由它们内部创建的函数访问,所以只要该函数存在就必须保留它们。

所以继续添加你想要的所有循环引用。除非您需要以IE6为目标,否则您的代码就可以了。