Question

In AS3, if a class is marked as dynamic, new properties can be added and removed at runtime, by simply setting the property or removing it with the delete keyword.

I am asking whether there is a faster way to determine whether a class is dynamic than calling the describeType function and checking the "isDynamic" attribute value on the returned top-level XML node, for example: <type name="flash.display::MovieClip" base="Class" isDynamic="true" isFinal="true" isStatic="true">.

I suspect there is a faster method, but all I really need to do is try to assign a property value if it exists or can be created.

//The "base is dynamic" test is pseudo-code since it's not valid
if (base.hasOwnProperty(propertyName) || (base is dynamic))
    base[propertyName] = value;
else
    throw new Error( "Property " + propertyName + " does not exist and cannot be created." );

Perhaps I would be better off just wrapping the assignment in a try/catch block and assuming the class is not dynamic when the assignment fails. If it succeeds, I don't care whether it's dynamic, since the goal is to simply assign the property value if exists or can be added.

try{base[propertyName] = value}catch(err:Error){/*property did not exist and class is not dynamic, or some other error occurred in the property setter*/}

My only issue with the try/catch approach is that I wouldn't know if the assignment failed because the property couldn't be assigned or if some other error occurred in the property setter. Even catching the error and checking its type will not tell me whether the error occurred at this precise point (as opposed to some other setter deep within this setters calling chain), because the getStackTrace method is only available in the debug player. That's why I really need to check whether the class is dynamic up front, so the assignment failure can be reliably predicted and avoided altogether. I will opt for a correct implementation over a faster one.

Was it helpful?

Solution 2

Since the only correct way to determine whether the property can be assigned is to check whether the property exists and whether the property can be created, I decided to focus on optimizing the determination of whether the instance is dynamic.

Although the describeType function may be relatively slow, I really only need to call it once per type if I cache the results. I could then store the boolean result in a dictionary by type name or class reference, and then just use the much faster functions getQualifiedClassName and/or getDefinitionByName methods to look up whether the class was dynamic.

public class ClassUtils
{
    static var typeDescriptions:Dictionary;
    static var isTypeDynamic:Dictionary;

    public function isDynamic( instanceOrClass:* ):Boolean
    {
        var qname:String = getQualifiedClassName(instanceOrClass);
        var isDynamic:* = isTypeDynamic[qname];
        if (isDynamic === undefined) //only explicitly untyped variables can hold the value undefined with strict equality
        {
            var desc:XML = getCachedTypeDescription( qname );
            isDynamic = Boolean(desc.@isDynamic);
            isTypeDynamic[qname] = isDynamic;
        }
        return isDynamic;
    }

    public function getCachedTypeDescription( qname:String ):XML
    {
        var desc:* = typeDescriptions[qname];
        if (desc === undefined) //only explicitly untyped variables can hold the value undefined with strict equality
        {
            desc = describeType( type );
            typeDescriptions[qname] = desc;
        }
        return desc;
    }
 }

That in turn will allow my original implementation to function quickly and efficiently:

if (base.hasOwnProperty(propertyName) || (ClassUtils.isDynamic(base))
    base[propertyName] = value;
else
    throw new Error( "Property " + propertyName + " does not exist and cannot be created." );

OTHER TIPS

My suggestion is to go the try/catch route. However, you actually can check to see if it failed because the property couldn't be assigned by either checking the errorID on the generic Error, OR you could catch that specific error before catching others. What you're looking for is 1056 which is a ReferenceError.

Here's an example of the 2nd method I mentioned:

var instanciatedSprite:Sprite = new Sprite();
var nonInstanciatedSprite:Sprite;
var dynamicMovieClip:MovieClip = new MovieClip();

for each(var obj:Object in [dynamicMovieClip, instanciatedSprite, nonInstanciatedSprite]){
    try{
        obj["abc"] = "abc";
    }
    catch(e:ReferenceError){
        trace("property did not exist and class is not dynamic");
    }
    catch(e:Error){
        trace("not the error you're looking for");
    }
}

This will first trace out property did not exist and class is not dynamic when it attempts to assign a property to the instanciatedSprite. Then, when it hits the nonInstanciatedSprite, it will skip that catch and get caught by the generic catch for all other Error types and trace out not the error you're looking for.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top