關於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操做.

相關文章
相關標籤/搜索