SharedObject Not saving data safely? - actionscript-3

I try to have an object with some immutable properties which initiate at the constructor and won't be able to change after initiation. Those properties then only have getter method. Since I want this object can be shared with other classes, I save it to the SharedObject class called saveIt().
All work fine when running the app on iOS (I do not test it on Android yet) except that if I completely shutdown the app from memory and reopen it, those properties' without setter are lost. The code is as below:
var rooms:ArrayCollection = saveData.read("rooms") as ArrayCollection;
if (!roomExists) {
var room:Room;
room = new Room(myID, myName, maxRoomSize);
room.name = rm.name;
room.timestamp = new Date();
room.joinUrl = rm.joinUrl;
room.attendeeUrl = rm.attendeeUrl;
room.attendees = conferenceParameters.metadata.hasOwnProperty("attendees") ? conferenceParameters.metadata["attendees"] : null;
rooms.addItem(room);
}
saveIt.save("rooms", rooms);

I found if I assign a dummy setter for each of the immutable properties i.e.
function set myID(s:String):void{}
then it can be safely saved even I shutdown the app. Since I can't find any information about this code behavior, so I'd like to look for opinions here!!

Related

Why does a closed NetConnection that has no event listeners or references stick around in memory?

It seems that if flash.net.NetConnection is instantiated and connected to an HTTP URL (such as an AMFPHP gateway), that instance is never picked up by garbage collection even after it has been closed and the only reference is set to null.
On the other hand, if the instance is connected to null (as would be done when used to play video/mp3 files), the instance is cleared from memory.
To clarify, the following connection will stick around in memory:
var stickyConn:NetConnection = new NetConnection();
stickyConn.connect("http://myserver/amfphp/gateway.php");
stickyConn.close();
stickyConn = null;
Whereas, the following connection will be cleared from memory immediately:
var tempConn:NetConnection = new NetConnection();
tempConn.connect(null);
tempConn.close();
tempConn = null;
Some things I have already tried to solve this issue:
set the client to an empty object (since the default value of the client is the NetConnection itself)
before closing the connection, call connect(null)
after closing the connection, call connect(null) and close it again
Has anyone run into this issue before? Is there a solution to this?
I have built heavyloaded FLV/Mp4 Players using AS3 quite often. When I am using a service like Akamai or Adobe's internal NetConnection Class I always keep in mind the
client object.
the is the property of NetConnection on which ALL callback methods are invoked. The default is this NetConnection instance this. If you set the client property to another object, callback methods will be invoked on that object.
In this way you can easily understand how Garbage Collection was never really applied accross each component in the same way. So, where stickyConn = null; only stops the playback, since you never declared a Weak Reference, Garbage Collection has no clue what to look for.
I have had success with differrent methods based on the specific player:
Simply stating NetConnectionObj.client = this usually suffices. But what if your NetConnection is extended or implementing an interface? Simply use a null Dictionary object:
var d:Dictionary = new Dictionary(true); . From here Garbage collection will recognize "d" as a weak reference and automatically dump it;
Hence, your snippet will look somewhat like this:
var Dc:Dictionary = new Dictionary(true);
NetConnection:NetConnection.client = Dc;
or some variation with the same intent.
I know this works, so reach out if you need help...
I may have been vague with the last answer in regards to GC and Dictionary Objects. Please take this snippet into consideration. I wrote it quickly but I try to explain the Concept of what solves your problem; mainly since I have dealt with it before:
public class Main extends MovieClip {
private var connection:NetConnection;
private var __nData:*;
private var _instance:*;
private var _closure:Function;
private var _D:Dictionary;
public function Main() {
connection = new NetConnection();
connection.addEventListener(NetStatusEvent.NET_STATUS, _nsHandle)
connection.connect(null);
}
public function _nsHandle(event:NetStatusEvent):void {
try {
connection = new NetConnection();
connection.connect(null);
connection.client = RegisterForGC(event.target);
RegisterForGC(connection);
} finally {
__nData = event.target.netConnection;
}
}
public function RegisterForGC(NCObject:*):* {
_instance = NCObject;
_closure = function ():void {}
_listener = function (e:Event):void {}
_D = new Dictionary(true);
_D[_listener] = "A";
_D[_instance] = "B";
_D[_closure] = "C";
try {
new LocalConnection().connect( "A" );
new LocalConnection().connect( "B" );
} catch (anything:*) { }
return _instance;
}
}
I'm not sure but Your example seems to suggest you are declaring your vars on stage / frame.
close(); is all you need for this to work HOWEVER....
from what I have found with NetConnection it for some reason unless all vars / functions are declared in an External class eg. public vars public function,
It stays in memory even after using close();
Pulled out my hair figuring this out with an audio streaming project.
However once I moved all coding to an external class, close(); actually closed the connection.
If your code is on a frame on stage or within MC I would create a class and declare vars & functions in said External Class.as and for some stupid reason it works.
Hope this helps.
Are you using a NetStream object and not disposing of it when finished? I only ask because I rarely see a NetConnection without a NetStream object far behind it.

Save Application state on Disk or some where so user access it later

In flex builder 4.5 i'm working on a project like cacoo.
I want to save diagrams(display object,ui components,text) before close the application into somewhere than I would be able to access after the application open again.
more clear:-If user edit some uml diagram on this project and save it for edit later and close application.after some days he/she want to edit previously saved diagram.
now how i'm save this diagram for future edit.
If save/open dialog will work for you, you can yse FileReference API. Before doing this, you have to implement serialization/deserialization of your state into/from String/ByteArray/XML object.
private var fileReference:FileReference;
// due to security restrictions, this method must be called from an
// event handler that responds to a user event (mouse click or key
// press), otherwise it will fail.
private function saveState(serializedState:*, fileName:String):void {
fileReference = new FileReference();
fileReference.addEventListener(Event.COMPLETE, onSaved);
fileReference.addEventListener(IOErrorEvent.IO_ERROR, onSavingError);
try {
fileReference.save(serializedState, fileName); // will open save dialog
} catch (e:Error) {
trace("error saving data: " + e.toString());
freeListeners();
}
}
private function onSaved(e:Event):void {
trace("saved!");
freeListeners();
}
private function onSavingError(e:ErrorEvent):void {
trace("error saving data: " + e.toString());
freeListeners();
}
private function freeListeners():void {
fileReference.removeEventListener(Event.COMPLETE, onSaved);
fileReference.removeEventListener(IOErrorEvent.IO_ERROR, onSavingError);
}
Similarly with restoring the state (use FileReference.browse(), then FileReference.load()).
If you need to save/restore app state without any dialogs, then you should probably use AIR (or SharedObject, as Raja Jaganathan suggested). But it seems to be not the case, as you want the user to be able to re-open the diagram in another system. To achieve this, you should allow the user to save his work to the appropriate place, so later he can move it to another machine/system and re-open it with your application.
Another alternative is to store everything on the server and provide the user with a list of saved files (like Cacoo does). If you go this way, you'll have to implement the corresponding server-side API. It may be REST API or smth like RTMP server. In the case of REST API, use FileReference.upload() to upload the data to your server, and URLLoader.load() to obtain it back.
You can store your diagram state through SharedObject for better you create one class which hold all of your state of Diagram so that later you can use
SharedObject using http://livedocs.adobe.com/flex/3/html/help.html?content=lsos_5.html
you can use registerClassAlias for custom class stored in sharedobject.
myClassInstance = new MyClass();
myClassInstance.x = 100;
myClassInstance.y = 100;
myClassInstance.text = "diagrams";
registerClassAlias("com.path.to.MyClass", MyClass);
myStuff = SharedObject.getLocal("myAppStuff");
myStuff.data.whatINamedIt = myClassInstance;
myStuff.flush();
now when get it back out... you can say:
myStuff = SharedObject.getLocal("myAppStuff");
var mySavedClass:MyClass = myStuff.data.whatINamedIt as MyClass;
Read mySavedClass instance value then inject to your diagram model when open again.
To implement application close event
http://www.flexer.info/2007/10/25/fabridge-warn-on-flex-application-exit/
Sprite or MovieClip other DisplayObject objects can not be direct serialized. So you should stored objects information (origin x,y, width, height, color, child info...). using a ByteArray or Array or Dictionary ... and that save to ShareObjects. later roll back from ShareObject and re-create Original Object. MovieClip or Sprite appropriate purpose is container.
Here is my test code.
1. create a Movieclip. purpose is container.
2. draw a rectangle using a graphics. And set the coordinates.
var drawWidth:Number = 500;
var drawHeight:Number = 300;
var rect:MovieClip = new MyRect();
rect.graphics.beginFill(0xffffff*Math.random(),1);
rect.graphics.drawRect(0,0,drawWidth,drawHeight);
rect.graphics.endFill();
rect.x= 300;
rect.y= 100;
3. Stores the information in the array.
var myRectInformation:Array = new Array();
myRectInformation.push(rect.x);
myRectInformation.push(rect.y);
myRectInformation.push(drawWidth);
myRectInformation.push(drawHeight);
var bmd:BitmapData = new BitmapData(rect.width, rect.height,true,0);
bmd.draw(rect);
//is byteArray.
myRectInformation.push(bmd.getPixels(new Rectangle(0,0,bmd.width,bmd.height)));
4. save to SharedObjects, array.
var mySaveData:SharedObject = SharedObject.getLocal("myStorage")
mySaveData.data.myRectInformation = myRectInformation;
mySaveData.flush();
5. this is load from SharedObject data stored. and recreate Objects.
var rect:MovieClip = new MyRect();
var loadBmd:BitmapData = new BitmapData(mySaveData.data.myRectInformation[2], mySaveData.data.myRectInformation[3], true, 1);
loadBmd.setPixels(new Rectangle(0,0,loadBmd.width,loadBmd.height), mySaveData.data.myRectInformation[4]);
var bmp:Bitmap = new Bitmap(loadBmd);
rect.addChild(bmp);
rect.x = mySaveData.data.myRectInformation[0];
rect.y = mySaveData.data.myRectInformation[1];
addChild(rect);

How to clone an object without knowing the exact type in AIR for iOS

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.
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:
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 ;)
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 );
}
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.

AS3/Flex - persistNavigatorState + objects with ArrayCollection()

I currently have a mobile application on the playbook that has the following class:
[Bindable]
public class Foo
{
public var myString:String;
public var myList:ArrayCollection;
public function Foo() {}
}
I also have persistNavigatorState="true" in my ViewNavigatorApplication.
Suppose in my first view I have the following in my creationComplete="init()" call:
private function init():void {
var s:String = "test_string";
var a:ArrayCollection = new ArrayCollection();
a.addItem("test1");
a.addItem("test2");
a.addItem("test3");
data.foo = new Foo();
data.foo.myString = s;
data.foo.myList = a;
trace(data.foo.myString);
trace(data.foo.myList[0]);
trace(data.foo.myList[1]);
trace(data.foo.myList[2]);
}
When executed, everything works fine in my app. However, since I want the sessions to persist in case the user accidentally closes the app, when he re-opens it the data should still be there.
Instead, when I close and re-open my app only the myString property persists (ie traces "test_string", as intended), however the ArrayCollection isn't copied.
I've tried the following with ObjectUtil.clone() and ObjectUtil.copy():
data.foo.myString = ObjectUtil.copy(s) as String;
data.foo.myList = ObjectUtil.copy(a) as ArrayCollection;
and I've also tried:
var f:Foo = new Foo();
f.myString = s;
f.myList = a;
data.foo = ObjectUtil.copy(f) as Foo;
trace(data.foo.myString);
trace(data.foo.myList[0]);
but this only throws me a
TypeError: Error #1009: Cannot access a property or method of a null object reference.
Any ideas on how to persist ArrayCollections and Foo class in a mobile application?
I'm not 100% sure, but I was wondering about this type of problem while working on a mobile app recently.
I believe your problem might be happening b/c your are setting the data manually on the View, instead of passing it into the View with the ViewNavigator.pushView() method.
I just browsed through the source, and it looks like setting the data directly on the View will bypass ViewNavigator's data persistence. Though with that said, I'm not sure why it would even remember the value for that String :)
I would try to do the following:
don't set the View's data property from inside the view, as you are doing now in the creationComplete handler
if possible, use the "firstView" property of ViewNavigatorApplication in mxml
if possible, initialize the "firstViewData" property in mxml (may not be possible)
if you can't do the two above, in your application's startup code call navigator.pushView(View_Class_Name, foo) to pass in the data.

Actionscript - Variable Assignment without reference?

Should be easy. I have an object. I want to modify it, but before i do I want to save a copy of it that I can go back to. I tried setting copy = original but when i modify the attributes of the original the copy also shows the changes. I am assuming this is because in actionscript any time you assign, it really just stores a reference to the original object. So whats the best way for me to store a copy of the original object for later use?
var newObj:Object = Object(ObjectUtil.copy(oldObj));
"Copies the specified Object and returns a reference to the copy. The copy is made using a native serialization technique. This means that custom serialization will be respected during the copy.
This method is designed for copying data objects, such as elements of a collection. It is not intended for copying a UIComponent object, such as a TextInput control. If you want to create copies of specific UIComponent objects, you can create a subclass of the component and implement a clone() method, or other method to perform the copy."
http://livedocs.adobe.com/flex/3/langref/mx/utils/ObjectUtil.html#copy()
What you are looking for is a deep copy of the object rather then passing by reference. I found the answer here which uses the new ByteArray class in AS3:
http://www.kirupa.com/forum/showthread.php?p=1897368
function clone(source:Object):* {
var copier:ByteArray = new ByteArray();
copier.writeObject(source);
copier.position = 0;
return(copier.readObject());
}
Which you then use like this:
newObjectCopy = clone(originalObject);
Cheers!
// duplicate any given Object (not MCs)
Object.prototype.copy = function()
{
ASSetPropFlags(Object.prototype,["copy"],1);
var _t = new this.__proto__.constructor(this) //
for(var i in this){
_t[i] = this[i].copy()
}
return _t
};
Usage
x = ["1","2","3",[4,5],[{a:1,b:2}]]
y = x.copy()
y[0] = 0
y[3][0]="d"
trace(x)
trace(y)