Why does delete( DictionaryInstance[ key ] ); fail?
Asked Answered
P

2

11

My app uses a Dictionary

protected _categoryToValueDict:Dictionary = new Dictionary();

to map something to something else.

Now, at a certain point in the application, I need to remove a certain key from the Dictionary.

I implemented this simple method:

    public function setCategoryNoValue( cat:TAModelCategory ):void {

        // delete( _categoryToValueDict[ cat ] );

        var old:Dictionary = _categoryToValueDict;

        _categoryToValueDict = new Dictionary();

        for ( var key:* in old ) {

            if ( key != cat ) {
                _categoryToValueDict[ key ] = old[ key ];
            }
        }

    }

If I only use [description of the delete operator]

delete( _categoryToValueDict[ cat ] );

the app itself doesn't throw errors in normal mode. But as soon as I serialize its external data structure to an external source [currently SharedObject], the app isn't able to de-serialize it later on.

If I use the above coded manual iterative removal operation, the de-serialize operation works as expected and the model appears in the app.

The alternative should be identical. Shouldn't they?

Thus, my question: What's the difference between the two alternatives?

PS: This question might be related to my previous one.

UPDATE-1

Adobe explains on this page:


To make the object referenced by myObject eligible for garbage collection, you must remove all references to it. In this case, you must change the value of myObject and delete the myObject key from myMap, as shown in the following code:

myObject = null;
delete myMap[myObject];

Is suppose this to be a typo. Shouldn't it read like this:

delete myMap[myObject];
myObject = null;

Why pass a null-pointer to myMap as key?

Pga answered 8/10, 2011 at 0:47 Comment(10)
+1 for a clearly explained question and doing some research before posting.Steinke
Reviewing your code; where is the source for the "delete" method?Steinke
@www.Flextras.com delete is an operator, not a method. Thus, I didn't provide a method.Pga
delete is not an operator listed on this list of ActionScript operators:help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/… . Nor is it listed in this list of statements, keywords, and directives: help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/… . Got any docs on what exactly it is?Steinke
@www.Flextras.com It's defined here: help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/… .Pga
Do you use methods as keys for your dictionary ? Because there's a bug with Dictionary: it stores the closure object as a key instead of the function itself, which means that you get duplicate entries if you try to update the value of an entry with a method as a key (since in fact the closure objects for the same method are not identical and therefore it creates 2 entries) Anyway, that definitely is one situation in which there would be a difference between your removal method and the delete operator.Antinomy
@Antinomy Thx, good to know! No, currently I use String objects as keys. They are the name of of the category. Values are custom object, which provide various details.Pga
how do you serialize your Dictionary? Also check the example prefixed by "The following example deletes the value of an array element, but the value of the length property is not changed" in the Adobe referenceStutsman
@StefanPantke, if you're using strings as keys, why aren't you simply using an object to store your key-value pairs?Cullie
@Cullie Because things might change later on.Pga
L
8

Okay, I just spent a good two hours or so looking into this, which is way more than I planning on spending looking at this. But I was intrigued.

I think you may have uncovered a legitimate bug in ActionScript's AMF encoding (or in how the Dictionary class gets seralized via AMF). The bug effects anything that uses AMF, so the exact same bug is reproduceable with a ByteArray, so I'm going to use that for demonstration purposes.

Consider the following code:

        var d:Dictionary = new Dictionary(false);
        d["goodbye"] = "world";
        d["hello"] = "world";
        delete d["hello"]

        var ba:ByteArray = new ByteArray();
        ba.writeObject(d);

        var len:uint = ba.position; 
        ba.position = 0;
        for(var i:uint=0;i<len;i++) {
            trace(ba.readUnsignedByte().toString(16));
        }

The output will be:

11 05 00 06 0f 67 6f 6f 64 62 79 65 06 0b 77 6f 72 6c 64

Now what if we don't ever put the "hello" in as a key:

        var d:Dictionary = new Dictionary(false);
        d["goodbye"] = "world";

        var ba:ByteArray = new ByteArray();
        ba.writeObject(d);

        var len:uint = ba.position; 
        ba.position = 0;
        for(var i:uint=0;i<len;i++) {
            trace(ba.readUnsignedByte().toString(16));
        }

The output then is:

11 03 00 06 0f 67 6f 6f 64 62 79 65 06 0b 77 6f 72 6c 64

Notice that the length is exactly the same, however they differ in the second byte.

Now lets look at the serialization for if I don't delete "hello":

11 05 01 06 0b 68 65 6c 6c 6f 06 0b 77 6f 72 6c 64 06 0f 67 6f 6f 64 62 79 65 06 02

Notice that 05 in the second byte is the same as when we deleted it. I think this is specifying the number of items in the Dictionary. I say "I think" because I dug through the documentation on AMF0/3 for quite a while trying to figure out exactly whats going on here, because it doesn't seem like this should be the serialization for a Dictionary, but its fairly consistent, but I don't get it.

So I think that's why you are hitting an exception (specifically the "End of file" error), because its still thinks there should be another item in the dictionary that it should be de-serializing.

Your alternate method works because you are constructing a new Dictionary and populating it... Its "internal counter" is only ever increasing, so it works like a charm.

Another thing to note, that if you set d["Hello"] = undefined, it does not throw an exception, but the item does not get removed from the dictionary. The key gets serialized with a value of undefined in the AMF stream. So the resulting byte-stream is longer than if it was never there.

Using an Object doesn't seem to exhibit this same behavior. Not only doesn't not produce an error, the generated bytecode is more in line with the AMF0/3 documentation I could find from Adobe. And the resulting "key" is literally dropped from the serialization, like it was in fact never there. So I'm not sure what special case they are using for Dictionary (apparently the undocumented AMF3 datatype 0x11), but it does not play right with deleting items out of it.

It seems like a legit bug to me.

edit

So I dug around a bit more and found other people talking about AMF serilization of a Dictionary.

0x11 : Dictionary Data Type
0x05 : Bit code: XXXX XXXY
     : If y == 0 then X is a reference to a previously encoded object in the stream
     : If y == 1 then X is the number of key/val pairs in the dictionary.

So if this case 5&1 == 1 and 5>>1 == 2, so it's expecting two key/val pairs in the "bad" serialized version.

Loveliesbleeding answered 2/11, 2011 at 20:21 Comment(2)
@SteAp: did you ever get around to filing a bug report? I hit the same issue today and I tried to search for a related bug report in the bug tracker but couldn't find any.Bevy
Hm, no, I filed a bug, but Adobe not even answered. Not sure, if the problem still exists in the current FB/AIR release. Need to check my sources, how I worked around the problem. Will come back later..Pga
S
0

Correct syntax for the delete operator is like this:

delete _categoryToValueDict[ cat ];

Although using parenthesis seems to compile fine, it's not the correct way.

Sweeper answered 8/10, 2011 at 8:38 Comment(2)
after some testing, it seems that using parenthesis work the same as not using them, so this may not work for you.Sweeper
In fact, the delete operator doesn't require parentheses. Since the parameter of delete is an expression and surrounding an expression with parenthesis its a valid expression too, it's perfectly legal to write delete( ... ).Pga

© 2022 - 2024 — McMap. All rights reserved.