Skip to content

Commit

Permalink
Merge pull request #1359 from pnstickne/wip-optimize-signal
Browse files Browse the repository at this point in the history
Signal - memory optimization / reductions
  • Loading branch information
photonstorm committed Nov 24, 2014
2 parents 5b757ea + e62442b commit 386c6df
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 40 deletions.
89 changes: 64 additions & 25 deletions src/core/Signal.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,54 +12,49 @@
* @constructor
*/
Phaser.Signal = function () {
};

Phaser.Signal.prototype = {

/**
* @property {Array.<Phaser.SignalBinding>} _bindings - Internal variable.
* @property {?Array.<Phaser.SignalBinding>} _bindings - Internal variable.
* @private
*/
this._bindings = [];
_bindings: null,

/**
* @property {any} _prevParams - Internal variable.
* @private
*/
this._prevParams = null;

// enforce dispatch to aways work on same context (#47)
var self = this;

/**
* @property {function} dispatch - The dispatch function is what sends the Signal out.
*/
this.dispatch = function(){
Phaser.Signal.prototype.dispatch.apply(self, arguments);
};
_prevParams: null,

/**
* If Signal should keep record of previously dispatched parameters and
* automatically execute listener during `add()`/`addOnce()` if Signal was
* already dispatched before.
* @property {boolean} memorize
*/
this.memorize = false;
memorize: false,

/**
* @property {boolean} _shouldPropagate
* @private
*/
this._shouldPropagate = true;
_shouldPropagate: true,

/**
* If Signal is active and should broadcast events.
* IMPORTANT: Setting this property during a dispatch will only affect the next dispatch, if you want to stop the propagation of a signal use `halt()` instead.
* @property {boolean} active
* @default
*/
this.active = true;

};
active: true,

Phaser.Signal.prototype = {
/**
* @property {function} _boundDispatch - The bound dispatch function, if any.
* @private
*/
_boundDispatch: true,

/**
* @method Phaser.Signal#validateListener
Expand Down Expand Up @@ -121,6 +116,11 @@ Phaser.Signal.prototype = {
*/
_addBinding: function (binding) {

if (!this._bindings)
{
this._bindings = [];
}

// Simplified insertion sort
var n = this._bindings.length;

Expand All @@ -137,10 +137,18 @@ Phaser.Signal.prototype = {
* @method Phaser.Signal#_indexOfListener
* @private
* @param {function} listener - Signal handler function.
* @param {object} [context=null] - Signal handler function.
* @return {number} The index of the listener within the private bindings array.
*/
_indexOfListener: function (listener, context) {

if (!this._bindings)
{
return -1;
}

if (typeof context === 'undefined') { context = null; }

var n = this._bindings.length;
var cur;

Expand Down Expand Up @@ -211,7 +219,7 @@ Phaser.Signal.prototype = {
*
* @method Phaser.Signal#remove
* @param {function} listener - Handler function that should be removed.
* @param {object} [context] - Execution context (since you can add the same handler multiple times if executing in a different context).
* @param {object} [context=null] - Execution context (since you can add the same handler multiple times if executing in a different context).
* @return {function} Listener handler function.
*/
remove: function (listener, context) {
Expand Down Expand Up @@ -240,6 +248,11 @@ Phaser.Signal.prototype = {

if (typeof context === 'undefined') { context = null; }

if (!this._bindings)
{
return;
}

var n = this._bindings.length;

while (n--)
Expand Down Expand Up @@ -273,7 +286,7 @@ Phaser.Signal.prototype = {
*/
getNumListeners: function () {

return this._bindings.length;
return this._bindings ? this._bindings.length : 0;

},

Expand All @@ -293,12 +306,14 @@ Phaser.Signal.prototype = {
/**
* Dispatch/Broadcast Signal to all listeners added to the queue.
*
* To create a bound dispatch for this Signal, use {@link Phaser.Signal#boundDispatch}.
*
* @method Phaser.Signal#dispatch
* @param {any} [params] - Parameters that should be passed to each handler.
*/
dispatch: function () {

if (!this.active)
if (!this.active || !this._bindings)
{
return;
}
Expand Down Expand Up @@ -338,7 +353,10 @@ Phaser.Signal.prototype = {
*/
forget: function() {

this._prevParams = null;
if (this._prevParams)
{
this._prevParams = null;
}

},

Expand All @@ -352,8 +370,11 @@ Phaser.Signal.prototype = {

this.removeAll();

delete this._bindings;
delete this._prevParams;
this._bindings = null;
if (this._prevParams)
{
this._prevParams = null;
}

},

Expand All @@ -370,4 +391,22 @@ Phaser.Signal.prototype = {

};

/**
* If the dispatch function needs to be passed somewhere, or called independently
* of the Signal object, use this function.
*
* @memberof Phaser.Signal
* @property {function} boundDispatch
*/
Object.defineProperty(Phaser.Signal.prototype, "boundDispatch", {

get: function () {
var _this = this;
return this._boundDispatch || (this._boundDispatch = function () {
return _this.dispatch.apply(_this, arguments);
});
}

});

Phaser.Signal.prototype.constructor = Phaser.Signal;
45 changes: 30 additions & 15 deletions src/core/SignalBinding.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* @param {Phaser.Signal} signal - Reference to Signal object that listener is currently bound to.
* @param {function} listener - Handler function bound to the signal.
* @param {boolean} isOnce - If binding should be executed just once.
* @param {object} [listenerContext] - Context on which listener will be executed (object that should represent the `this` variable inside listener function).
* @param {object} [listenerContext=null] - Context on which listener will be executed (object that should represent the `this` variable inside listener function).
* @param {number} [priority] - The priority level of the event listener. (default = 0).
*/
Phaser.SignalBinding = function (signal, listener, isOnce, listenerContext, priority) {
Expand All @@ -26,51 +26,66 @@ Phaser.SignalBinding = function (signal, listener, isOnce, listenerContext, prio
*/
this._listener = listener;

if (isOnce)
{
this._isOnce = true;
}

if (listenerContext != null) /* not null/undefined */
{
this.context = listenerContext;
}

/**
* @property {boolean} _isOnce - If binding should be executed just once.
* @property {Phaser.Signal} _signal - Reference to Signal object that listener is currently bound to.
* @private
*/
this._isOnce = isOnce;
this._signal = signal;

if (priority)
{
this._priority = priority;
}

};

Phaser.SignalBinding.prototype = {

/**
* @property {object|undefined|null} context - Context on which listener will be executed (object that should represent the `this` variable inside listener function).
* @property {?object} context - Context on which listener will be executed (object that should represent the `this` variable inside listener function).
*/
this.context = listenerContext;
context: null,

/**
* @property {Phaser.Signal} _signal - Reference to Signal object that listener is currently bound to.
* @property {boolean} _isOnce - If binding should be executed just once.
* @private
*/
this._signal = signal;
_isOnce: false,

/**
* @property {number} _priority - Listener priority.
* @private
*/
this._priority = priority || 0;
_priority: 0,

/**
* @property {number} callCount - The number of times the handler function has been called.
*/
this.callCount = 0;
callCount: 0,

/**
* If binding is active and should be executed.
* @property {boolean} active
* @default
*/
this.active = true;
active: true,

/**
* Default parameters passed to listener during `Signal.dispatch` and `SignalBinding.execute` (curried parameters).
* @property {array|null} params
* @default
*/
this.params = null;

};

Phaser.SignalBinding.prototype = {
params: null,

/**
* Call listener passing arbitrary parameters.
Expand Down

0 comments on commit 386c6df

Please sign in to comment.