关于as3实现对任何对象进行深入复制的思考

  无心中看到关于as3深度复制思考的文章,以为不错,因而转来记录之后用到能够参考。数组

 转载来源:http://xmchcly.iteye.com/blog/1307425,下面是转文:函数

经过 ByteArray 能够对数组和 Object 进行深度复制,脚本网上满天飞:工具

var obj:Object = new Object();测试

var ba:ByteArray = new ByteArray();
ba.writeObject(obj);
ba.position = 0;
var obj2:Object = ba.readObject();.net

我就想了,能不能用这种方法对任何对象都进行深度复制呢?orm

试试这样,通常来讲网上说了是对数组和 Object,那么对于其它类型多半都会有问题: xml

var s:Sprite = new Sprite();对象

var ba:ByteArray = new ByteArray();
ba.writeObject(s);
ba.position = 0;
var s2:Sprite = ba.readObject();blog

嗯,果真抛错了:继承

Exception fault: TypeError: Error #1034: 强制转换类型失败:没法将 Object@128a309 转换为 flash.display.Sprite。

那咱们再改一下看看
var s:Sprite = new Sprite();

var ba:ByteArray = new ByteArray();
ba.writeObject(s);
ba.position = 0;
var s2:Sprite = ba.readObject() as Sprite;
运行,等于没改,异常依旧。

好了,不买关子了,直接上个能用的,用全局函数 registerClassAlias 注册一下 Sprite 便可:


//关键就是这句
registerClassAlias("flash.display.Sprite", Sprite);

var s:Sprite = new Sprite();
s.x = 100;

var ba:ByteArray = new ByteArray();
ba.writeObject(s);
ba.position = 0;
var s2:Sprite = ba.readObject() as Sprite;

trace("复制的对象 x : ", s2.x);

s2.x = 200;

trace("源的对象 x : ", s.x, "复制的对象 x : ", s2.x);

咱们看看输出了什么:

复制的对象 x :  100
源的对象 x :  100 复制的对象 x :  200

哦也,对象成功的复制出来了,等等,好像还输出了什么,待我看看先:

TypeError: Error #1034: 强制转换类型失败:没法将 Object@12883f9 转换为 flash.media.SoundTransform。
TypeError: Error #1034: 强制转换类型失败:没法将 Object@1288629 转换为 flash.geom.Transform。

我转换的是 Sprite 呀,关这个两个类型啥事?后来想了想,貌似 Sprite 里有 本身的 soundTransform 属性和继承自 DisplayObject 的 transform 属性,那好办呀,把他俩也注册一下不就好了。

但是这样仍是很麻烦,毕竟咱们要把深度复制的对象用到的类都注册一遍,有没有什么办法能够取出该类包括其父类 import 的全部类呢,这里咱们能够用另外的一个全局函数 describeType,这个函数吧一个类或类的实例的全部详细说明都取出做为 xml 对象返回,你们能够看看这个返回的数据就知道了:

var xml:XML = describeType(new Sprite());
trace(xml);


好了咱们建立一个函数把该类包括其父类 import 的类都取出来吧:
public static function getAllQualifiedClassName($object:*):Array{
        if($object == null){
                throw new ArgumentError("Parameter $object can not be null.");
                return null;
        }else{
                var xml:XML = describeType($object), dictionary:Dictionary = new Dictionary(), i:int, j:int, result:Array = new Array();
                dictionary[xml.@name] = true;
                for(i=0; i<xml.extendsClass.length(); i++){
                        dictionary[String(xml.extendsClass[i].@type)] = true;
                }
                for(i=0; i<xml.implementsInterface.length(); i++)
                        dictionary[String(xml.implementsInterface[i].@type)] = true;
                for(i=0; i<xml.accessor.length(); i++)
                        dictionary[String(xml.accessor[i].@type)] = true;
                for(i=0; i<xml.method.length(); i++){
                        dictionary[String(xml.method[i].@returnType)] = true;
                        for(j=0; j<xml.method[i].parameter.length(); j++)
                                dictionary[String(xml.method[i].parameter[j].@type)] = true;
                }
                for(var obj:* in dictionary)
                        if(String(obj) != "void" && String(obj) != "*")
                                result.push(String(obj));
                return result;
        }
}
考虑到会有不少地方用到就把它静态了,注意 void 和 * 须要剔除,咱们取到了全部须要注册的类,接下来就是把他们一并注册了事,嘿嘿嘿:


var allClassName:Array = HObjectUtils.getAllQualifiedClassName(new Sprite());
for each(var item:String in allClassName)
        registerClassAlias(item, Class(getDefinitionByName(item)));

var s:Sprite = new Sprite();
s.x = 100;

var ba:ByteArray = new ByteArray();
ba.writeObject(s);
ba.position = 0;
var s2:Sprite = ba.readObject() as Sprite;

trace("复制的对象 x : ", s2.x);

s2.x = 200;

trace("源的对象 x : ", s.x, "复制的对象 x : ", s2.x);

但是没想到一运行fp就给了我当头一棒:

Exception fault: ArgumentError: Error #1063: flash.geom::Transform() 的参数数量不匹配。应该有 1 个,当前为 0 个。

没想到 ByteArray 的 writeObject 和 readObject 对构造函数带必填参数的对象有先天性的支持缺陷,也就是说若是你要深度复制的对象或它的一个属性指向的对象的构造函数是须要参数的话,很差意思了, writeObject 和 readObject 可没办法传参数,这个方法将会失效,可是我可不甘心,若是说不支持带参数的构造函数的话,我就试试不带参数的状况下这个方法走不走得通,至于构造函数带有 参数的深度复制能够寻找其它的路径解决。

这里我弄了个例子:

深度复制类:ObjectUtils

package {
        import flash.utils.getQualifiedClassName;
        import flash.utils.Dictionary;
        import flash.utils.describeType;
        import flash.utils.getDefinitionByName;
        import flash.net.registerClassAlias;
        import flash.utils.ByteArray;

        public class ObjectUtils {
               
                public static function cloneObject($object:*):*{
                        if($object == null){
                                throw new ArgumentError("Parameter $object can not be null.");
                                return null;
                        }else{
                                //为了深度复制一个对象, 须要注册该对象用到的全部类
                                var allClassName:Array = getAllQualifiedClassName($object);
                                for each(var item:String in allClassName)
                                        registerClassAlias(item, Class(getDefinitionByName(item)));
                                //使用 byteArray 对象进行深度复制便可
                                var bytes:ByteArray = new ByteArray();
                                bytes.writeObject($object);
                                bytes.position = 0;
                                //取出复制出的新对象,若是有带参数的构造函数而致使复制失败的就返回 null
                                var className:String = getQualifiedClassName($object);
                                var ObjectClass:Class = Class(getDefinitionByName(className));
                                var result:*;
                                try{
                                        result = bytes.readObject() as ObjectClass;
                                        return result;
                                }catch($error:ArgumentError){
                                        trace($error.message);
                                }
                                return null;
                        }
                }
               
                public static function getAllQualifiedClassName($object:*):Array{
                        if($object == null){
                                throw new ArgumentError("Parameter $object can not be null.");
                                return null;
                        }else{
                                var xml:XML = describeType($object), dictionary:Dictionary = new Dictionary(), i:int, j:int, result:Array = new Array();
                                dictionary[xml.@name] = true;
                                for(i=0; i<xml.extendsClass.length(); i++){
                                        dictionary[String(xml.extendsClass[i].@type)] = true;
                                }
                                for(i=0; i<xml.implementsInterface.length(); i++)
                                        dictionary[String(xml.implementsInterface[i].@type)] = true;
                                for(i=0; i<xml.accessor.length(); i++)
                                        dictionary[String(xml.accessor[i].@type)] = true;
                                for(i=0; i<xml.method.length(); i++){
                                        dictionary[String(xml.method[i].@returnType)] = true;
                                        for(j=0; j<xml.method[i].parameter.length(); j++)
                                                dictionary[String(xml.method[i].parameter[j].@type)] = true;
                                }
                                for(var obj:* in dictionary)
                                        if(String(obj) != "void" && String(obj) != "*")
                                                result.push(String(obj));
                                return result;
                        }
                }
               
        }
}
人物类:Person

package {
        import flash.utils.ByteArray;

        public class Person {
               
                private var _name:Name = new Name();
                private var _age:int = 1;
               
                private var _bytes:ByteArray;
               
                public function Person(){
                }
               
                public function set name(v:Name):void{
                        _name = v;
                }
                public function get name():Name{
                        return _name;
                }
               
                public function set age(v:int):void{
                        _age = v;
                }
                public function get age():int{
                        return _age;
                }
               
                public function set bytes(v:ByteArray):void{
                        _bytes = v;
                }
                public function get bytes():ByteArray{
                        return _bytes;
                }
               
                public function talk():String{
                        _bytes.position = 0;
                        var dream:String = "";
                        while(_bytes.position<_bytes.length)
                                dream += _bytes.readUTF();
                        return "我叫"+_name.x+_name.m+",我"+_age+"岁,个人梦想是"+dream;
                }
               
        }
}

名字类:Name

package {

        public class Name {
               
                private var _x:String = "";
                private var _m:String = "";
               
                public function Name(){
                }
               
                public function set x(v:String):void{
                        _x = v;
                }
                public function get x():String{
                        return _x;
                }
               
                public function set m(v:String):void{
                        _m = v;
                }
                public function get m():String{
                        return _m;
                }
               
        }
}


主类:Main
package {
        import flash.utils.ByteArray;
        import flash.events.Event;
        import flash.display.Sprite;

        public class Main extends Sprite {
               
                public function Main(){
                        if(stage)
                                init();
                        else
                                addEventListener(Event.ADDED_TO_STAGE, init);
                }
               
                private function init(e:Event=null):void{
                        var a:Person = new Person();
                        var aName:Name = new Name();
                        a.name = aName;
                        a.age = 20;
                        aName.x = "张";
                        aName.m = "三";
                        a.bytes = new ByteArray();
                        a.bytes.writeUTF("娶个媳妇!");
                        trace("源对象:", a.talk());
                       
                        var b:Person = ObjectUtils.cloneObject(a) as Person;
                        trace("深度复制后的对象:", b.talk());
                       
                        trace("复制出的对象改变值了!!!看看复制出的对象的值的改变会否影响到源对象!!!");
                       
                        b.name.x = "李";
                        b.name.m = "四";
                        b.age = 22;
                        b.bytes.position = b.bytes.length;
                        b.bytes.writeUTF("再买栋房子!");
                       
                        trace("源对象:", a.talk());
                        trace("深度复制后的对象:", b.talk());
                }
               
        }
}

咱们看看输出:

源对象: 我叫张三,我20岁,个人梦想是娶个媳妇!
深度复制后的对象: 我叫张三,我20岁,个人梦想是娶个媳妇!
复制出的对象改变值了!!!看看复制出的对象的值的改变会否影响到源对象!!!
源对象: 我叫张三,我20岁,个人梦想是娶个媳妇!
深度复制后的对象: 我叫李四,我22岁,个人梦想是娶个媳妇!再买栋房子!

为了测试我特地弄了一个 Name 类又使用了 ByteArray 对象,如今看来一切运行良好,惟一的遗憾就是不能支持构造函数带参数的对象的深度复制,关键是只要须要复制的对象和构造函数带参数的对象拉上哪怕一点点关 系也要失败,这样一来该 ObjectUtils 工具类的使用面就大打折扣了。

不知道各位仙家有没有更好的方法真正实现任意对象的深度复制,还有本文若有错误也请不吝指教。

另一种方法的思考:是否能够用 describeType 取出对象的全部属性后在 new 出一个该对象,再把源对象的值逐一复制到新的对象上,若是有属性是引用了另外一个对象就在用一样的方法建立该新对象的,直到全部属性都指向新建立的对象为 止,这样咱们就能够在 new 该对象时把构造函数的参数添加上去了。

另外说一下,用 ByteArray 复制对象时只复制该对象的 public 属性,若是你有一个私有属性,而且该私有属性是经过一个非 set 函数进行赋值的话,该私有属性的值则不会被复制到新的对象中。


======================================================

 

 

 

 

 

 

 

 

最近有点儿时间,帖点儿小经验给你们.有不妥之处敬请包涵.
用byteArray克隆对象,估计你们大多数都用过.应该是相似下面的实现
01.var copier:ByteArray = new ByteArray();

02.            copier.writeObject(source);

03.            copier.position = 0;

04.            return copier.readObject();
复制代码但对于一个至关复杂的对象,要达到彻底克隆,还面临着不少问题.
好比下面这几种状况:
1:属性是自定义类型
2.属性是数组,并且数组元素是自定义类型
3.属性是接口形式,但现实是某种实现
4.属性是Dictionary
等等.不知道你们是否碰到过这样的状况.
因此通过研究,对上面的方法进行了一些拓展.造成了下面这种方式进行克隆.


private static function regtype(tn:String):void {
            if (tn == null || tn == "null" || tn == "int" || tn == "string" || tn == "Number" || tn == "String" || tn == "Boolean" || tn == "Object")return;
            var type:Class;
            try {
                type = getClassByAlias(tn);
            } catch(err:Error) {
            }
            if (type != null)return;
            try {
                type = Class(getDefinitionByName(tn));
            } catch(err:Error) {
                return;
            }
            if (type == null)return;
            registerClassAlias(tn, type);
        }

        private static function registerClass(source:*):void {
            var tn:String = getQualifiedClassName(source);
            regtype(tn);
            if(tn=="Array"||tn=="flash.utils::Dictionary"){
                for(var ele:String in source){
                    registerClass(source[ele]);
                }
            }
            var dxml:XML = describeType(source);
            for each(var acc:XML in dxml.accessor) {
                registerClass(source[acc.@name]);
            }
            for each(var acc1:XML in dxml.variable) {
                registerClass(source[acc1.@name]);
            }
            for each(var acc2:XML in dxml.implementsInterface) {
                regtype(acc2.@type);
            }
            regtype(dxml.extendsClass.@type);
        }

        public static function baseClone(source:*):* {
            registerClass(source);
            var copier:ByteArray = new ByteArray();
            copier.writeObject(source);
            copier.position = 0;
            return copier.readObject();
        }

其中baseClone是入口方法,其他两个作辅助工做.具体原理是:1.用describeType方法对原始对象进行结构分析2.把原始对象的复杂属性注册别名3.递归进行2操做.

相关文章
相关标签/搜索