7

バックボーンには、現在のイベントアグリゲーターを使用するアプリがありwindow.App.Events ます。多くのビューで、そのアグリゲーターにバインドします。ビューに破棄関数を手動で記述しました。この関数は、そのイベントアグリゲーターからのバインド解除を処理してから、ビューを削除します。 。(ビューを直接削除する代わりに)。

現在、この機能も必要なモデルがいくつかありましたが、それに取り組む方法がわかりません。

特定のモデルは特定のイベントにバインドする必要がありますが、私は間違っているかもしれませんが、コレクションからモデルを削除すると、まだ存在しているイベントアグリゲーターへのこれらのバインドにより、モデルはメモリに残ります。

ビューのように、モデルには実際には削除機能はありません。だから私はこれにどのように取り組むでしょうか?

リクエストに応じて編集 、いくつかのコード例。

App = {
    Events: _.extend({}, Backbone.Events)
};

var User = Backbone.Model.extend({

    initialize: function(){
        _.bindAll(this, 'hide');
        App.Events.bind('burglar-enters-the-building', this.hide);
    },

    hide: function(burglarName){
        this.set({'isHidden': true});
        console.warn("%s is hiding... because %s entered the house", this.get('name'), burglarName);
    }

});

var Users = Backbone.Collection.extend({

    model: User

});

var House = Backbone.Model.extend({

    initialize: function(){
        this.set({'inhabitants': new Users()});
    },

    evacuate: function(){
        this.get('inhabitants').reset();
    }

});



$(function(){

    var myHouse = new House({});

    myHouse.get('inhabitants').reset([{id: 1, name: 'John'}, {id: 1, name: 'Jane'}]);

    console.log('currently living in the house: ', myHouse.get('inhabitants').toJSON());

    App.Events.trigger('burglar-enters-the-building', 'burglar1');

    myHouse.evacuate();

    console.log('currently living in the house: ', myHouse.get('inhabitants').toJSON());

    App.Events.trigger('burglar-enters-the-building', 'burglar2');

});​

jsFiddleで実際に動作しているこのコードを表示します(コンソールに出力):http://jsfiddle.net/saelfaer/szvFY/1/

ご覧のとおり、モデルのイベントではなく、イベントアグリゲーターにバインドします。モデル自体からイベントのバインドを解除する必要はありません。モデルが削除された場合、誰もモデルでイベントを再度トリガーすることはないためです。ただし、アプリ全体でイベントを簡単に渡すことができるように、eventAggregatorは常に配置されています。

コード例は、コレクションから削除された場合でも、家には住んでいないが、泥棒が家に入ったときに非表示コマンドを実行することを示しています。

4

3 に答える 3

13

バインド イベントの方向がObject1 -> listen -> Object2の場合でも、 Object1が生きている参照を失うためには削除する必要があることがわかります。

Model イベントをリッスンすることは、呼び出しremoveで呼び出されないため、解決策ではないことがわかります。2 つの解決策があります。Collection.reset()

1.通常のコレクションのクリーンアップを上書きします

@dira sais hereのようCollection._removeReferenceに、メソッドをより適切にクリーニングするために上書きできます。

次の 2 つの理由から、このソリューションは好きではありません。

  • 後で呼び出さなければならないメソッドを上書きするのは好きではありませんsuper
  • プライベート メソッドを上書きしたくない

Collection.reset()2.通話のオーバーラップ

これは逆です。より深い機能を追加する代わりに、上位の機能を追加します。

次に、直接呼び出す代わりに、サイレントに削除される前にモデルをクリーンアップCollection.reset()する実装を呼び出すことができます。

cleanUp: function( data ){
  this.each( function( model ) { model.unlink(); } );
  this.reset( data );
} 

コードのソーター バージョンは次のようになります。

AppEvents = {};
_.extend(AppEvents, Backbone.Events)

var User = Backbone.Model.extend({
  initialize: function(){
    AppEvents.on('my_event', this.listen, this);
  },

  listen: function(){
    console.log("%s still listening...", this.get('name'));
  },

  unlink: function(){
   AppEvents.off( null, null, this );
  }
});

var Users = Backbone.Collection.extend({
  model: User,

  cleanUp: function( data ){
    this.each( function( model ) { model.unlink(); } );
    this.reset( data );
  }
});


// testing
var users = new Users([{name: 'John'}]);
console.log('users.size: ', users.size()); // 1
AppEvents.trigger('my_event');             // John still listening...

users.cleanUp();
console.log('users.size: ', users.size()); // 0
AppEvents.trigger('my_event');             // (nothing)

jsFiddleを確認してください。

更新: binding-event リンクを削除した後にモデルが削除されたことの確認

まず、Object2 のイベントをリッスンしているObject1 がObect2 -> Object1の方向にリンクを作成することを確認します。

私たちのオブジェクトは保持されます

上の画像では、モデル (@314019) がusersコレクションによって保持されているだけでなく、AppEvents観察しているオブジェクトに対しても保持されていることがわかります。プログラマーの観点からリンクしているイベントはObject that listen -> to -> Object that is listenのように見えますが、実際には完全に反対です: Object that is listen -> to -> Object that is listen .

を使用しCollection.reset()て Collection を空にすると、usersリンクが削除されていることがわかりますが、AppEventsリンクは残っています。

オブジェクトは保持されます 2

usersリンクは消えており、リンクも仕事のOurModel.collection一部だと思います。Collection._removeReference()

Collection.cleanUp()メソッドを使用すると、オブジェクトがメモリから消えます。オブジェクト @314019 が削除されChrome.profileたことを明示的に伝えるツールを作成することはできませんが、メモリ オブジェクトの中になくなっていることがわかります。

于 2012-05-03T14:01:55.173 に答える
1

fguillen が提案したようにCollection'sresetをラップする代わりに、直接拡張してオーバーライドすることを好みます。その理由は 、クライアントのコードでのみ効果があり、ライブラリ (つまりBackbone ) では効果がないためです。たとえば、を内部的に呼び出すことができます。バックボーンのソース コードを変更しない限り、 を呼び出した後に ( のように)イベントからモデルをアンバインドすることはできません。cleanUpCollectionresetcleanUpCollection.fetchCollection.resetcleanUpCollection.fetch

基本的に、私の提案するスニペットは次のとおりです。

var MyCollection = Backbone.Collection.extend({
        reset: function(models, options) {
            this.each(function(model) {
                model.unlink(); // same as fguillen's code
            });
            Backbone.Collection.prototype.reset.apply(this, arguments);
        }
    });

後で、に基づいて新しいコレクションを作成できますMyCollection

于 2012-09-07T10:21:07.253 に答える
1

クリーン リファレンス プロセスは のトリッキーな部分だと思いますBackbone

コレクションModelからを削除すると、コレクション自体がバインドされているモデルのイベントが処理されます。このプライベート Collection メソッドを確認してくださいCollectionunbind

おそらく、アグリゲーターで同じ手法を使用できます。

// ... Aggregator code
the_model.on( "remove", this.unlinkModel, this );
// ... more Aggregator code

unlinkModel: function( model ){
  model.off( null, null, this );
}

これは、バインディングの方向がAggregator -> Modelの場合です。方向が逆であれば、モデルを取り外した後にクリーニングを行う必要はないと思います。

于 2012-05-03T10:57:27.407 に答える