Skip to content

Instantly share code, notes, and snippets.

@lynxerzhang
Last active December 15, 2015 16:48
Show Gist options
  • Save lynxerzhang/5291265 to your computer and use it in GitHub Desktop.
Save lynxerzhang/5291265 to your computer and use it in GitHub Desktop.
package
{
import flash.utils.Dictionary;
/**
* 1.robertPenner的Signal如果在遍历中直接修改存储signal的链表, 则不会反映到当前遍历中(会在下一次派发中体现)
* 2.PureMVC中存储Observer的数组, 最终进行播报Notification时, 也是如此(先复制一份当前结构的副本,以避免播报过程中数组索引被修改)
* 3.RichardLord的Ash框架中使用链表结构实现了自己的Signal, 可以实现在播报Signal监听方法时, 直接修改(删除,添加)链表结构
* 4.jacksondunstan的turbosignals利用了内置的数组结构, 和robertPannner的Signal一样, 但是和PureMVC不同的是,
* jacksondunstan直接使用了数组的内置方法进行复制, 而不是通过循环。
*
* 该类的dispatch方法参考Starling框架的Juggler动画管理类对存储在内部数组的对象的动态管理。
*
* @example
*
* var s:Signals = new Signals;
* s.add(test1);
* s.add(test2);
* s.add(test3);
* ... ...
* function test2():void{
* s.remove(test3); //直接在播报中删除test3, test3方法将不再被执行
* s.add(test4); //该方法为新添加, 所以本轮的播报不会触发, 需要在下一次的
* //dispatch中触发
* }
* s.dispatch();
*/
public class Signals
{
public function Signals()
{
}
/**
* 记录是否只执行一次即删除
*/
private var onceCheck:Dictionary = new Dictionary(false);
/**
* 记录所有监听方法
*/
private var base:Vector.<Function> = new <Function>[];
/**
* 记录监听方法数量
*/
private var count:int = 0;
/**
* 添加一个监听方法
* @param fun
* @param executeOnce
*/
public function add(fun:Function, executeOnce:Boolean = false):void{
if(!contain(fun)){
onceCheck[fun] = new Node(executeOnce, base.push(fun) - 1);
count++;
}
}
/**
* 移除一个方法
*/
public function remove(fun:Function):void{
if(contain(fun)){
var node:Node = onceCheck[fun];
base[node.index] = null; //将值设为null,不使用splice,以免破坏dispatch中的循环操作。
delete onceCheck[fun];
count--;
}
}
/**
* 检查是否存在某个方法对象
*/
public function contain(fun:Function):Boolean{
return fun in onceCheck;
}
/**
* 获取监听对象数量
*/
public function get length():int{
return count;
}
/**
* 移除所有监听方法
*/
public function removeAll():void{
for(var fun:* in onceCheck){
remove(fun);
}
}
/**
* TODO
* @param args
*/
public function dispatch(...args):void{
if(isEmpty()){
return;
}
var len:int = base.length, i:int, actual:int, fun:Function;
for(i = 0; i < len; i++){
fun = base[int(i)];
if(fun != null){
if(args.length == 0){
fun();
}
else if(args.length == 1){
fun(args[0]);
}
else{
fun.apply(null, args); //apply有可能会影响性能, 所以判断方法参数长度
}
//有可能监听方法中存移除操作破坏了循环, 所以需要判断是否为空
if(base[int(i)] != null){
if(onceCheck[fun].executeOnce){
remove(fun); //if execute once, just remove it
}
if(actual < i){
base[int(actual)] = fun;
onceCheck[fun].index = actual;
base[int(i)] = null;
}
actual++;
}
}
}
if(i != actual){
len = base.length;
while(i < len){
if(base[int(i)] != null){
base[int(actual)] = base[int(i)];
onceCheck[base[int(actual)]].index = actual;
base[int(i)] = null;
actual++;
}
i++;
}
base.length = actual;
}
}
/**
* 检查是否该Signal对象内部存储结构为空
*/
public function isEmpty():Boolean{
return count == 0;
}
/**
* 获取指定function对象的索引
* @param fun
*/
public function getIndex(fun:Function):int{
if(contain(fun)){
return onceCheck[fun].index;
}
return -1;
}
}
}
/**
* 记录方法对象在数组中的索引和"是否执行一次即删除"的节点对象
*/
internal class Node{
public var executeOnce:Boolean = false;
public var index:int = -1;
public function Node(once:Boolean, index:int):void{
this.executeOnce = once;
this.index = index;
}
}
@demoth
Copy link

demoth commented Apr 2, 2013

i was trying to understand the comments but failed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment