Не можете наследовать от классов, определенных в RSL? - PullRequest
3 голосов
/ 24 июня 2010

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

У меня есть класс A, определенный в RSL (технически это художественный актив, который я сделал во Flash IDE и экспортировал для actioncript. Затем весь .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 {}

1 Ответ

2 голосов
/ 27 июня 2010

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

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

var bar:B = new B(); 

на

var bar:B;

Вы все равно получите сообщение об ошибке.

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

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

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

-compiler.library-path "lib.swc"

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

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

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

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

Возможность избежать этой проблемы (вообще не используя RSL) может иметь shell.swf, который загружает искусство.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());
    }

}

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

...