2

I am writing an iOS game in Flash and I need a way to clone polymorphic objects. I have BaseClass, SubClass1, SubClass2 (and so on...) and I need a clone() method in BaseClass, that will create a copy of the current object, without a conditional such as

var obj:BaseClass;
if(this is SubClass1) { 
   obj = new SubClass1(); 
}else if(this is SubClass2) {
   obj = new SubClass2();
}else...

I need a way to create an object and create the exact bytes (yes, a shallow copy is enough for my purpose) of the object. I've looked at:

AS3 - Clone an object

As3 Copy object

http://actionscripthowto.com/how-to-clone-objects-in-as3/

But none seem to work. Probably not available in AIR 3.3 for iOS SDK. (they compile, but the code doesn't work in my case)

Is there any other way, or did anybody achieve to clone an object in AIR for iOS?

Thanks,

Can.

Community
  • 1
  • 1
Can Poyrazoğlu
  • 33,241
  • 48
  • 191
  • 389
  • There are several ways to do this, but first please explain why this needs to be restricted to the base class - it seems like the obvious choice would be to override the clone() method for each subtype and manually copy all the fields. – weltraumpirat Jul 22 '12 at 10:07
  • I don't need anything extra to do in subclasses, where there will be many of them. so a clone() method that copies the bits will be enough for my purpose. If I need to add anything in any subclass' clone method, I'd just override it and add the behavior. – Can Poyrazoğlu Jul 22 '12 at 10:09

2 Answers2

1

Bit-by-bit cloning cannot be done with ActionScript, unless your class only contains primitive values (i.e. a simple data structure). That's what the ByteArray approach you've linked to in this question's answer is used for - but when you're dealing with complex types, especially display objects, you'll soon come to the limits (as, I gather, you have already realized).

So this more or less leaves you with two options:

  1. Create a new object and copy all of its fields and properties.

    This is the way to go if you're going to need behavior and field values, and you didn't use any drawing methods (i.e., you can not copy vector graphics this way). Creating a new class instance without knowing its exact type can be done in a generalized way using reflections, getQualifiedClassName() and getDefinitionByName() will help you there, and if you need more than just the name, describeType(). This does have limits, too, though:private fields will not be available (they don't appear in the information provided by describeType()), and in order to not run into performance problems, you will have to use some sort of cacheing. Luckily, as3commons-reflect has already solved this, so implementing the rest of what you need for a fully functional shallow copy mechanism is not too complex.

    Create a new instance like this:

    var newObject:* = new Type.forInstance( myObject ).clazz();
    

    Then iterate over all accessors, variables and dynamic properties and assign the old instance's values.

    I have implemented a method like this myself, for an open source framework I am working on. You can download or fork it at github. There isn't any documentation yet, but its use is as simple as writing:

    var myCopy:* = shallowCopy( myObject );
    

    I also have a copy() method there, which creates a true deep copy. This, however, has not been tested with anything but data structures (albeit large ones), so use at your own risk ;)

  2. Create a bitmap copy.

    If you do have vector graphics in place, this is often easier than recreating an image: Simply draw the content of the object's graphics to a new Bitmap.

    function bitmapCopy( source:Sprite ):Bitmap {
        source.cacheAsBitmap = true;
        var bitmapData:BitmapData = new BitmapData( source.width, source.height, true, 0xFFFFFF );
        bitmapData.draw( source, new Matrix(), null, null, null, true );
    
        return new Bitmap( bitmapData, PixelSnapping.AUTO, true );
    }
    
Community
  • 1
  • 1
weltraumpirat
  • 22,544
  • 5
  • 40
  • 54
  • I'll be glad to use (and test) your library. But this project will go commercial (I'm planning to sell the game for a dollar or two in App Store), would using your library in my project cause any license issues? – Can Poyrazoğlu Jul 22 '12 at 15:14
  • 1
    Nah. It's in a very early stage, and I haven't added any license information as of yet, but it will be under the MIT license. I would appreciate an honorable mention of some sort, but that's certainly not a requirement. – weltraumpirat Jul 22 '12 at 15:19
  • I've used your library. it is really easy to use, and it manages to create a new object of the same type. however, it didn't copy any properties from the old object to the new one. it just created a new instance of the same type (which I was trying to achieve in the first place), but none of the fields are copied to the new object. – Can Poyrazoğlu Jul 22 '12 at 16:14
  • I honestly don't know how to put something to github, but here is the only code that I have: http://canpoyrazoglu.com/Smiley.as.zip StandardSmileys derives (and doesn't add anything currently) from this (as an in-Flash-designed MovieClip with AS3 export option), and the code I'm using is function copySmiley(smi:Smiley) : Smiley{ var newsmi:Smiley = shallowCopy(smi); return newsmi; } – Can Poyrazoğlu Jul 22 '12 at 16:25
  • Okay, I see you haven't used the accessor conventions for AS3 - `getField()` and `setField()` won't be recognized as such. See this: http://fupeg.blogspot.de/2007/11/actionscript-getters-and-setters.html – weltraumpirat Jul 22 '12 at 16:34
  • Oh, I didn't know it required accessors to run properly. so would it work if I convert it to the as3 accessor syntax? isn't there a way to copy fields directly from the other object? – Can Poyrazoğlu Jul 22 '12 at 17:00
  • Yes, using regular accessors works fine for all I can say. As do public variables. You could iterate over all public methods, check for "get..." and "set..." in the method name and then copy the values if you find both. But you'd have to do this yourself - I will not support unconventional code, and I very much doubt other libraries do. There usually is a reason for conventions to exist... ;) – weltraumpirat Jul 22 '12 at 17:05
  • Ok, I'll be converting my code. will write here if I face any problems though ;) – Can Poyrazoğlu Jul 22 '12 at 17:11
0

You need to create an abstract clone method in the base class and implement it for each subclass. In the specific implementations, you would copy all of the properties of the object to the new one.

public class BaseClass {
    public function clone():BaseClass
    {
         // throw an error so you quickly see the places where you forgot to override it
         throw new Error("clone() should be overridden in subclasses!");
         return null;
    }
}

public class Subclass1 extends BaseClass {
    public override function clone():BaseClass
    {
         var copy:Subclass1 = new Subclass1();
         copy.prop1 = prop1;
         copy.prop2 = prop2;
         // .. etc
         return copy;
    }
}

If you wanted to create a generic default implementation of clone, you could use describeType to access the properties and copy them over:

public function clone():BaseClass
{
    var defn:XML = describeType(this);
    var clsName:String = defn.@name;
    var cls:Class = getDefinitionByName(clsName) as Class;
    var inst:* = new cls();
    for each(var prop:String in (defn.variable + defn.accessor.(@access == 'readwrite')).@name )
    {
        inst[prop] = this[prop];
    }
    return inst;
}

The main issue with this is that the describeType XML can get quite large - especially if you are dealing with objects that extend DisplayObject. That could use a lot of memory and be slow on iOS.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
  • No, this is explicitly not I want. I need a generic default implementation **that will actually create the object in memory** by looking at the current type of the object. (without conditionals) such as var newobj:BaseClass = createBitByBitExactCopy(derivedClassInstance); where I don't know the exact type of the derived instance. I just know it is a derived class of the base class. – Can Poyrazoğlu Jul 22 '12 at 10:57
  • You cannot access objects in memory. If you want to create a generic clone, you can do something similar to what I added in my edit. Otherwise, you can use `ByteArray.writeObject()` but you'll still have to do a bit of extra work, and it's not copying memory. – Peter Hall Jul 22 '12 at 11:00
  • ByteArray.writeObject is actually exactly what I want, but I can't seem to get it working on AIR 3.3 for iOS. – Can Poyrazoğlu Jul 22 '12 at 11:02