Копирование простых объектов
Чаще всего ByteArray используют для копирования объектов. AMF сериализатор и десериализатор (не люблю эти слова, но не нашёл подходящего перевода) доступен через ByteArray API. Для дублирования объектов с помощью ByteArray мы будем использовать методы writeObject и readObject:
// Создаём пустой ByteArray var stream:ByteArray = new ByteArray(); // Создаём объект var parameters:Object = { age : 25, name : "Bob" }; // «Переводим» объект в формат AMF и сохраняем его в ByteArray stream.writeObject( parameters ); // Сбрасываем позицию stream.position = 0; // Считываем объект var objectCopy:Object = stream.readObject();
На основе этого кода можно создать функцию, которую мы сможем использовать для копирования объекто��:
function copyObject(objectToCopy:*):* { var stream:ByteArray = new ByteArray(); stream.writeObject( objectToCopy ); stream.position = 0; return stream.readObject(); }
Чтобы удостовериться, что мы действительно создали отдельную копию объекта, мы можем модифицировать свойства исходного объекта:
// Создаём объект var parameters:Object = { age : 25, name : "Bob" }; var parametersCopy:Object = copyObject ( parameters ); parameters.nom = "Stevie"; /* outputs : name : Bob age : 25 */ for ( var p:String in parametersCopy ) { trace( p, " : ", parametersCopy[p] ); } function copyObject ( objectToCopy:* ):* { var stream:ByteArray = new ByteArray(); stream.writeObject( objectToCopy ); stream.position = 0; return stream.readObject(); }
Чуть ниже мы узнаем, как можно сохранять и восстанавливать более сложные типы данных, с помощью ByteArray.
Копирование пользовательских классов
Код, описанный ранее, не будет работать для пользовательских классов, которые вы можете использовать в вашем приложении. Разберём очень простой пример, скажем, вам нужно использовать объекты класса User:
package { public class User { private var _firstName:String; private var _lastName:String; public function set firstName (firstName:String):void { _firstName = firstName; } public function set lastName (lastName:String):void { _lastName = lastName; } public function get firstName ():String { return _firstName; } public function get lastName ():String { return _lastName; } } }
Вам может понадобиться сохранить объект класса User на сервере в виде бинарных данных или локально в SharedObject. Если вы попробуете сохранить объект класса User в ByteArray и извлечь его, то при чтении, Flash Player будет проверять, был ли зарегистрирован тип User ранее, если нет, то этот объект будет «разбираться» как обычный объект:
// Создаём экземпляр класса User var myUser:User = new User (); // Устанавливаем свойства myUser.firstName = "Stevie"; myUser.lastName = "Wonder"; // outputs :[object User] trace ( myUser ); // Создаём ByteArray для сохранения экземпляра var bytes:ByteArray = new ByteArray(); // Сохраняем экземпляр bytes.writeObject ( myUser ); // Сбрасываем позицию bytes.position = 0; // outputs : false trace ( bytes.readObject() is User );
Чтобы зарегистрировать тип User в Flash Player можно использовать метод registerClassAlias. После этого объекты класса User можно будет «разбирать» с помощью ByteArray.
// Создаём экземпляр класса User var myUser:User = new User (); // Устанавливаем свойства myUser.firstName = "Stevie"; myUser.lastName = "Wonder"; // outputs :[object User] trace ( myUser ); // Регистрируем тип User для «разбора» registerClassAlias ( "userTypeAlias", User ); // Создаём ByteArray для сохранения экземпляра var bytes:ByteArray = new ByteArray(); // Сохраняем экземпляр bytes.writeObject ( myUser ); // Сбрасываем позицию bytes.position = 0; // При «разборе» сохраненённого объекта, автоматически будет использоваться нужный тип var storedUser:User = bytes.readObject() as User; // outputs : true trace ( storedUser is User ); // outputs : Stevie Wonder trace ( storedUser.firstName, storedUser.lastName );
Используя эту технику мы можем сохранять и восстанавливать экземпляры любых пользовательских классов. Как вы можете увидеть, сейчас мы работаем с копией объекта пользовательского класса:
storedUser.firstName = "Bobby"; storedUser.lastName = "Womack"; // outputs : Stevie Wonder trace ( myUser.firstName, myUser.lastName ); // outputs : Bobby Womack trace ( storedUser.firstName, storedUser.lastName );
Нужно помнить, что некоторые нативные типы данных не могут быть сериализованы/десериализованы с помощью AMF. Это относится к таким визуальным объектам, например DisplayObject. Например, если вы попробуете «разобрать» экземпляр MovieClip (если бы это можно было сделать, то это было бы очень круто =), то у вас ничего не получится:
// Создаём ByteArray var bytes:ByteArray = new ByteArray(); // Пытаемся сохранить объетк DisplayObject bytes.writeObject ( new MovieClip() ); // Сбрасываем позицию bytes.position = 0; // outputs : undefined trace ( bytes.readObject() );
Поддержка визуальных объектов в AMF3 была бы очень полезным дополнением для списка поддерживаемых в AMF типов.
От переводчика
Мне нравится «упаковывать» все часто используемые функции в отдельные классы со статическими функциями и данный случай с копированием объектов не стал исключением =) Для этого я создал небольшой класс и попытался сохранить описанным способом некоторый свой класс, в ходе небольшого эксперимента я наткнулся на небольшой нюанс, который не описан в исходной статье: если конструктор классов нужного вам типа требует передачи обязательных параметров, то при «восстановлении» объекта возникнет ошибка, связанная с тем, что в конструктор не были переданы нужные данные. Благо, данная проблема не возникает, если конструктор требует передачи параметров, но они не являются обязательными.
Кстати, у себя в примере, я попробовал скопировать Array и Vector., и у меня всё это получилось). Необходимость сохранения/чтения данных в SharedObject у меня возникает довольно часто, поэтому, думаю, что данный способ разбора и копирования сложных классов будет мне ��чень и очень полезен.
Исходник примера
