代码之家  ›  专栏  ›  技术社区  ›  Trace

循环引用内存泄漏?

  •  2
  • Trace  · 技术社区  · 11 年前

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

    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 回复  |  直到 11 年前
        1
  •  3
  •   hashchange    11 年前

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

    更长的答案:陷阱可能在代码中 没有 张贴。特别是,必须正确清理视图中的任何事件处理程序,否则视图 never fade into oblivion 。但是你在问题中说过你要清理你的观点,所以我想你已经意识到了这种问题。

        2
  •  3
  •   Ravi Hamsa    11 年前

    控制器在这里做的事情对我来说就像一个实用程序。可以很容易地由全局级的单例进行管理。我第一眼就看到了一些问题。

    • 代码重复,假设您将为不同类型的模型和视图创建单独的控制器,则需要为每个控制器重复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)
     }
    
        3
  •  1
  •   Richard Connamacher    11 年前

    JavaScript实现很长一段时间没有遇到循环引用的问题。(如果我没记错的话,IE6确实存在循环引用导致的内存泄漏,这在那个时期没有被任何其他主要浏览器共享。)

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

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

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

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

    因此,继续添加所有需要的循环引用。除非你需要瞄准IE6,否则你的代码就可以了。