Не можете наследовать от классов, определенных в RSL?

Примечание. Это проект ActionScript, а не проект Flex.

У меня есть класс A, определенный в RSL (технически это художественный актив, который я создал во Flash IDE и экспортировал для ActionScript. Затем весь .FLA был экспортирован как SWC / SWF).

В моем основном проекте у меня есть класс B, который наследуется от класса A. Проект компилируется нормально, без ошибок.

Однако когда при запуске проекта я пытаюсь создать экземпляр класса B, я получаю сообщение об ошибке проверки. Однако создание экземпляра класса A работает нормально:

import com.foo.graphics.A;   // defined in art.swf / art.swc
import com.foo.graphics.B;   // defined locally, inherits from A
...
<load art.SWF at runtime>
...
var foo:A = new A(); // works fine
var bar:B = new B(); // ERROR!
// VerifyError: Error #1014: Class com.foo.graphics::A could not be found.

Для справки вот как я загружаю RSL:

var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onArtLoaded);
var request:URLRequest = new URLRequest("art.swf");
var context:LoaderContext = new LoaderContext();
context.applicationDomain = ApplicationDomain.currentDomain;
loader.load(request, context);

Класс B определяется следующим образом:

import com.foo.graphics.A;
class B extends A {}

person Ender    schedule 24.06.2010    source источник
comment
Похоже на ошибку компилятора - удаление моего ответа, чтобы он остался без ответа и мог привлечь больше внимания. Насколько я могу судить, ошибки проверки вызваны ошибками в компиляторе Liveocs .adobe.com / flash / 9.0 / ActionScriptLangRefV3 / amarghosh.blogspot .com / 2009/04 / verifyerror-1102.html Они говорят, что это происходит из-за искаженного SWF, но я говорю, что если компилятор дает вам искаженный SWF, это ошибка в компиляторе.   -  person Amarghosh    schedule 24.06.2010
comment
Это должно нормально работать. Фактически, у меня есть проект с аналогичной настройкой, скомпилированный с помощью mxmlc из Flex 4, и он работает.   -  person back2dos    schedule 24.06.2010
comment
Может это проблема компилятора Flash CS4 IDE? Он был выпущен примерно в то же время, что и Flex 3, и поэтому должен иметь полную поддержку RSL, но, знаете ли ... это Adobe. Попробую с mxmlc и посмотрю, как пойдет.   -  person Ender    schedule 24.06.2010


Ответы (1)


Не думаю, что это ошибка. Это скорее проблема связи.

Ошибка верификатора не возникает, когда вы пытаетесь создать экземпляр B. Это происходит, как только ваш основной swf загружается и проверяется плеером. Это важное различие. Чтобы понять, что я имею в виду, измените этот код:

var bar:B = new B(); 

to

var bar:B;

Вы по-прежнему будете получать сообщение об ошибке.

Я не знаю, как вы создаете swf, но из ошибки кажется очевидным, что класс A (родитель B) исключается из swf. Я могу воспроизвести это с помощью этого переключателя mxmlc:

-compiler.external-library-path "lib.swc"

Однако изменив его на:

-compiler.library-path "lib.swc"

Проблема уходит. Очевидно, что этот вид побеждает цель загрузки ресурсов во время выполнения, поскольку эти ресурсы уже скомпилированы в ваш main.swf (на самом деле, это хуже, потому что, сделав это, вы только что увеличили глобальный размер загрузки вашего приложения) .

Итак, если вы установите свою художественную библиотеку как внешнюю, компилятор сможет выполнять проверку типов, вы получите автозаполнение в вашей среде IDE и т. Д. Однако ваш класс B по-прежнему зависит от определения A. Итак, во время выполнения A должен быть определен всякий раз, когда B впервые упоминается в вашем коде. В противном случае верификатор обнаружит несогласие и взорвется.

Во Flash IDE, когда вы связываете символ, есть опция «экспорт в первом кадре». Именно так ваш код экспортируется по умолчанию, но это также означает, что можно отложить, когда игрок впервые ссылается на определение класса. Flex использует это для предварительной загрузки. Он загружает только крошечный бит swf, достаточный для отображения анимации предварительного загрузки, в то время как остальная часть кода (который не «экспортируется в первом кадре») и ресурсы загружаются. Делать это вручную кажется немного обременительным, если не сказать больше.

Теоретически использование RSL должно помочь здесь, если я правильно помню, как работает RSL (идея заключается в том, что RSL должен загружаться игроком прозрачно). По моему опыту, RSL - это настоящая боль, и она не стоит того (просто чтобы назвать несколько раздражающих «особенностей»: вам нужно жестко запрограммировать URL-адреса, довольно сложно сделать недействительными кеши, когда это необходимо, и т. Д. Возможно, некоторые из проблем с RSL ушли, и сейчас эта штука работает разумно, но я могу сказать вам, что работаю с Flash, начиная с Flash 6 и на протяжении многих лет, время от времени я развлекался идеей использования RSL (потому что сама идея дает много смысла, реализация в стороне), только чтобы отказаться от нее после обнаружения одной проблемы за другой.

Возможность избежать этой проблемы (вообще без использования RSL) может заключаться в наличии shell.swf, который загружает art.swf и после его загрузки загружает ваш текущий код. Поскольку к моменту загрузки вашего code.swf art.swf уже загружен, верификатор найдет com.foo.graphics.A (в art.swf) при проверке com.foo.graphics.B (в code.swf).

    public function Shell()
    {
        loadSwf("art.swf",onArtLoaded);
    }

    private function loadSwf(swf:String,handler:Function):void {
        var loader:Loader = new Loader();
        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, handler);
        var request:URLRequest = new URLRequest(swf);
        var context:LoaderContext = new LoaderContext();
        context.applicationDomain = ApplicationDomain.currentDomain;
        loader.load(request, context);              
    }

    private function onArtLoaded(e:Event):void {
        loadSwf("code.swf",onCodeLoaded);
    }   

    private function onCodeLoaded(e:Event):void {
        var li:LoaderInfo = e.target as LoaderInfo;
        addChild(li.content);

    }

В вашем текущем основном классе добавьте этот код:

        if (stage) init();
        else addEventListener(Event.ADDED_TO_STAGE, init);

Переместите логику конструктора (если есть) в метод init, и он должен работать нормально.

Но что мне не нравится в этом подходе, так это то, что вам нужно создать другой проект для оболочки.

Как правило, у меня есть класс, который проксирует графический ресурс.

    private var _symbol:MovieClip;

    public function B() {
        var symbolDef:Class = ApplicationDomain.currentDomain.getDefinition("com.foo.graphics.A") as Class;
        _symbol= new symbolDef();
        addChild(_symbol);
    }

Поскольку com.foo.graphics.A - это просто графический ресурс, вам действительно не нужно проксировать другие вещи. Я имею в виду, что если вы хотите изменить x, y, width и т. Д. И т. Д., Вы можете просто изменить эти значения в прокси-сервере, и на практике результат будет таким же. Если в каком-то случае это не так, вы можете добавить геттер / сеттер, который фактически действует на проксируемый объект (com.foo.graphics.A).

Вы можете абстрагировать это в базовый класс:

public class MovieClipProxy extends MovieClip {

    private var _symbol:MovieClip;

    public function MovieClipProxy(linkagetName:String) {
        var symbolDef:Class = ApplicationDomain.currentDomain.getDefinition(linkagetName) as Class;
        _symbol = new symbolDef();          
        addChild(_symbol);
    }

    //  You don't actually need these two setters, but just to give you the idea...
    public function set x(v:Number):void {
        _symbol.x = v;
    }

    public function get x():Number {
        return _symbol.x;
    }
}

public class B extends MovieClipProxy {

    public function B() {
        super("com.foo.graphics.A");
    }


}    

Кроме того, внедрение домена приложения в качестве зависимости (и перемещение механизма создания экземпляра в другой служебный класс) может быть полезно для некоторых проектов, но приведенный выше код подходит в большинстве ситуаций.

Единственная проблема с этим подходом заключается в том, что имя связи в конструкторе B не проверяется компилятором, но, поскольку оно находится только в одном месте, я думаю, что с этим можно справиться. И, конечно же, вы должны убедиться, что ваша библиотека ресурсов загружена, прежде чем пытаться создать экземпляр класса, который зависит от нее, иначе он покажет ожидание. Но в остальном это сработало для меня довольно хорошо.

PS

Я только что понял, что в вашем текущем сценарии это может быть более простое решение:

public class B extends MovieClip {

    private var _symbol:MovieClip;

    public function B() {
        _symbol = new A();
        addChild(_symbol);
    }

}

Или просто:

public class B extends MovieClip {

    public function B() {
        addChild(new A());
    }

}

Та же идея с прокси, но вам не нужно беспокоиться о создании экземпляра объекта из строки с использованием домена приложения.

person Juan Pablo Califano    schedule 26.06.2010
comment
Вау, отличный ответ! В качестве дополнения: после прочтения спецификации AVM2 я знаю причину VerifyError. JPC совершенно прав в том, что ошибка проверки выдается, когда проигрыватель загружает фильм, а НЕ во время выполнения. Когда фильм загружен, проигрыватель не выполняет много проверок, но одна из вещей, которые ДЕЙСТВУЮТ, - это то, что все цепочки наследования классов ДОЛЖНЫ быть определены. Таким образом, вы не можете наследовать от класса, определение которого еще не было загружено. Обратите внимание, что это не применяется к членам класса, статическим членам или переменным в стеке. - person Ender; 28.06.2010
comment
(продолжение) Таким образом, класс Foo может содержать ссылки на объекты класса B (определенные извне). Он просто не может наследовать от класса B, если вы вручную не загрузили определение класса B в текущий ApplicationDomain до того, как будет загружен класс Foo, ссылающийся на фильм. Предлагаемые решения для JVC: 1). Напишите предварительный загрузчик, который сначала загружает вашу внешнюю библиотеку, а затем ваш основной фильм. 2). Вместо этого попросите ваш класс добавить экземпляр B в качестве дочернего элемента. Поскольку он не наследуется от B, просто используя его, проблем не возникнет. - person Ender; 28.06.2010