|
Macromedia Flash非公式テクニカルノート
ObjectCopy.copy()メソッドを多重配列対応に修正する
ID: FN0604001 |
Product: Flash |
Platform: All
Version: Flash MX 2004 and above
ObjectCopy.copy()メソッドは、通常参照渡しとなるオブジェクトを複製して返します。しかし、配列を複製しようとする場合、エレメントの配列がさらに配列を含む多重配列は複製されず、参照が格納されてしまいます。本稿ではObjectCopyクラスを、多重配列に対応できるよう修正してみます。
1. 現状の多重配列を複製した結果
エレメントの配列がさらに配列を含む多重配列を定義して、ObjectCopy.copy()メソッドにより複製すると、エレメントのインスタンスは複製されず、参照が格納されます。
つぎのサンプル(スクリプト001)では、配列_arrayのエレメント0の配列[0]は複製されて、複製先copy_arrayの配列エレメント0に格納されています。しかし、エレメント1は[[1], [2, 3]]とそのエレメント中にさらに配列を含むため、複製されず参照が格納されています。
スクリプト001■ObjectCopy.copy()で多重配列を複製する
// フレームアクション
import mx.utils.ObjectCopy;
var _array:Array = [[0], [[1], [2, 3]]];
var copy_array:Object = ObjectCopy.copy(_array);
// [デバッグ] > [変数のリストアップ]:
変数 _level0._array = [オブジェクト #4, クラス 'Array'] [
0:[オブジェクト #5, クラス 'Array'] [
0:0
],
1:[オブジェクト #6, クラス 'Array'] [
0:[オブジェクト #7, クラス 'Array'] [
0:1
],
1:[オブジェクト #8, クラス 'Array'] [
0:2,
1:3
]
]
]
変数 _level0.copy_array = [オブジェクト #9, クラス 'Array'] [
0:[オブジェクト #10, クラス 'Array'] [
0:0
],
1:[オブジェクト #11, クラス 'Array'] [
0:[オブジェクト #7, クラス 'Array'],
1:[オブジェクト #8, クラス 'Array']
]
]
|
2. ObjectCopy.copy()メソッドが多重配列を複製しない原因
グローバルクラスパス(「クラスパスの考え方」「6.2 グローバルおよびドキュメントレベル(document-level)クラスパス」参照)のConfiguration/Classesフォルダ内のmx/utils/に、ObjectCopy.asクラス定義ファイルが格納されています。
クラス(静的)メソッドObjectCopy.copy()メソッドは、引数として渡された複製元オブジェクトのコンストラクタ関数を呼出して複製先となる新規インスタンスを作成し、そのインスタンスと複製元オブジェクトをクラス(静的)メソッドObjectCopy.copyProperties()に渡してプロパティを複製します(スクリプト002)。
スクリプト002■mx.utils.ObjectCopyのクラス定義
class mx.utils.ObjectCopy {
public static function copy( refObj:Object ):Object {
var result:Object = new Function( refObj.__proto__.constructor)();
copyProperties( result, refObj );
return( result );
} // copy
public static function copyProperties( dstObj:Object, srcObj:Object ):Void {
var to:String;
for( var i in srcObj ) {
to=typeof( srcObj[i] );
if( to != "function" ) {
if( to == "object" ) {
if( srcObj[i] instanceof Array ) { // [1]配列インスタンスの場合
var p:Array = new Array();
var q:Array = srcObj[i];
for( var j:Number=0; j<q.length; j++ )
p[j]= q[j]; // エレメントをそのまま格納
dstObj[i] = p;
}
else
if( srcObj[i] instanceof String )
dstObj[i] = new String( srcObj[i] );
else
if( srcObj[i] instanceof Number )
dstObj[i] = new Number( srcObj[i] );
else
if( srcObj[i] instanceof Boolean )
dstObj[i] = new Boolean( srcObj[i] );
else // [2]その他オブジェクトの場合
dstObj[i] = copy( srcObj[i] ); // copy()メソッドを再帰呼出し
}
else
dstObj[i] = srcObj[i];
} // if not a function
} // for
} // copyProperties
}
|
ObjectCopy.copyProperties()メソッドは、オブジェクトから取出したプロパティが[1]配列インスタンスの場合、複製先としての新規配列は作成するものの、配列エレメントについてはそれが配列などのオブジェクトかどうかを考慮することなく単純に新規配列に格納しています。そのため、取出したプロパティの配列エレメントにさらに配列やオブジェクトを含むとき、その複製は行わずに参照が格納されることになります。
これは取出したプロパティが[2](StringやNumber、Booleanを除く)その他のオブジェクトの場合に、ObjectCopy.copy()メソッドを再帰呼出ししているのと対照的です。
3. ObjectCopy.copyProperties()メソッドの修正
前述ObjectCopy.copyProperties()の処理中で、取出したプロパティが[1]配列の場合と[2]その他オブジェクトの場合とを区別する意味はないように思われます。
ObjectCopy.copy()メソッドは、複製元オブジェクトのクラスを判別して新規インスタンスを作成しています。複製元オブジェクトが配列なら、新規Arrayインスタンスを作成します。したがって、そのエレメント(プロパティ)を取出して複製する処理については、その他のオブジェクトととくに異なるところがないからです(プロパティ名が配列では数値で、その他のオブジェクトでは文字列だという違いがあるに過ぎません)。
配列を別途に扱う処理を外し、その他オブジェクトと同様に複製するよう修正したのが、つぎのスクリプト003です。
スクリプト003■修正したObjectCopy.copyProperties()メソッド
class mx.utils.ObjectCopy {
public static function copy(refObj:Object):Object {
var result:Object = new Function(refObj.__proto__.constructor)();
copyProperties(result, refObj);
return (result);
} // copy
public static function copyProperties(dstObj:Object, srcObj:Object):Void {
var to:String;
for (var i:String in srcObj) {
var myObject:Object = srcObj[i];
// to = typeof (srcObj[i]);
to = typeof (myObject);
if (to != "function") {
if (to == "object") {
/* 配列の扱いはその他のオブジェクトと共通に
if (srcObj[i] instanceof Array) {
var p:Array = new Array();
var q:Array = srcObj[i];
for (var j:Number = 0; j<q.length; j++) {
p[j] = q[j];
}
dstObj[i] = p;
} else if (srcObj[i] instanceof String) {
*/
if (myObject instanceof String) {
// dstObj[i] = new String(srcObj[i]);
dstObj[i] = new String(String(myObject));
// } else if (srcObj[i] instanceof Number) {
} else if (myObject instanceof Number) {
// dstObj[i] = new Number(srcObj[i]);
dstObj[i] = new Number(myObject);
// } else if (srcObj[i] instanceof Boolean) {
} else if (myObject instanceof Boolean) {
// dstObj[i] = new Boolean(srcObj[i]);
dstObj[i] = new Boolean(myObject);
} else {
// dstObj[i] = copy(srcObj[i]);
dstObj[i] = copy(myObject); // 配列もその他オブジェクトとして複製
}
} else {
// dstObj[i] = srcObj[i];
dstObj[i] = myObject;
}
} // if not a function
} // for
} // copyProperties
}
|
この機会に、何度もアクセスするプロパティは、ローカル変数に取出しました。また、もとのスクリプトではステートメントブロック{}が明記されておらず、誤解や見落としを生じやすいので、これを加えることにしました。
つぎのサンプルは、修正したObjectCopy.copyProperties()メソッドを使って、多重配列の複製を試した結果です(スクリプト004)。
スクリプト004■修正したObjectCopy.copyProperties()メソッドのテスト結果
// フレームアクション
import mx.utils.ObjectCopy;
var _array:Array = [[[0]], [[new Number(1)], [2, {a:[3], b:[new Boolean(true), "c"]}]]];
var copy_array:Object = ObjectCopy.copy(_array);
// [デバッグ] > [変数のリストアップ]:
変数 _level0._array = [オブジェクト #4, クラス 'Array'] [
0:[オブジェクト #5, クラス 'Array'] [
0:[オブジェクト #6, クラス 'Array'] [
0:0
]
],
1:[オブジェクト #7, クラス 'Array'] [
0:[オブジェクト #8, クラス 'Array'] [
0:[オブジェクト #9, クラス 'Number'] {1}
],
1:[オブジェクト #10, クラス 'Array'] [
0:2,
1:[オブジェクト #11, クラス 'Object'] {
b:[オブジェクト #12, クラス 'Array'] [
0:[オブジェクト #13, クラス 'Boolean'] {true},
1:"c"
],
a:[オブジェクト #14, クラス 'Array'] [
0:3
]
}
]
]
]
変数 _level0.copy_array = [オブジェクト #15, クラス 'Array'] [
0:[オブジェクト #16, クラス 'Array'] [
0:[オブジェクト #17, クラス 'Array'] [
0:0
]
],
1:[オブジェクト #18, クラス 'Array'] [
0:[オブジェクト #19, クラス 'Array'] [
0:[オブジェクト #20, クラス 'Number'] {1}
],
1:[オブジェクト #21, クラス 'Array'] [
0:2,
1:[オブジェクト #22] {
a:[オブジェクト #23, クラス 'Array'] [
0:3
],
b:[オブジェクト #24, クラス 'Array'] [
0:[オブジェクト #25, クラス 'Boolean'] {true},
1:"c"
]
}
]
]
]
|
_____
作成者: 野中文雄
作成日: 2006年4月4日
Copyright ©
2001-2006 Fumio Nonaka. All rights reserved.
|