3

注:これはActionscriptプロジェクトであり、Flexプロジェクトではありません。

RSLでクラスAを定義しています(技術的には、Flash IDEで作成し、ActionScript用にエクスポートしたアートアセットです。その後、.FLA全体がSWC / SWFとしてエクスポートされました)。

私のメインプロジェクトには、クラスAから継承するクラスBがあります。プロジェクトはエラーなしで正常にコンパイルされます。

ただし、プロジェクトを実行してクラス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 {}
4

1 に答える 1

2

これはバグではないと思います。それはもっとリンケージの問題です。

のインスタンスを作成しようとしても、ベリファイアエラーは発生しません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にコンパイルされているため、この種のアセットを実行時にロードする目的は無効になります(実際、これを行うことで、アプリのグローバルダウンロードサイズが増加したため、さらに悪いことになります) 。

したがって、art libを外部として設定すると、コンパイラは型チェックを実行でき、IDEでオートコンプリートを取得できます。ただし、BクラスはA定義されているかどうかに依存します。したがって、実行時に、コードで最初に参照されるときはAいつでも定義する必要があります。Bそうしないと、検証者は不整合を見つけて爆破します。

Flash IDEでは、シンボルをリンクするときに、「最初のフレームでエクスポート」オプションがあります。これは、デフォルトでコードがエクスポートされる方法ですが、クラスの定義がプレーヤーによって最初に参照される時期を延期することも可能であることを意味します。Flexはこれをプリロードに使用します。残りのコード(「最初のフレームでエクスポート」されない)とアセットがロードされている間、プリローダーアニメーションを表示するのに十分な、ほんの少しのswfをロードします。控えめに言っても、これを手作業で行うのは少し面倒に思えます。

理論的には、RSLがどのように機能するかを正しく思い出せば、RSLを使用すると、ここで役立つはずです(RSLであるという考えは、プレーヤーによって透過的にロードされる必要があります)。私の経験では、RSLは非常に苦痛であり、面倒な価値はありません(いくつかの厄介な「機能」を挙げれば、URLをハードコーディングする必要があり、必要に応じてキャッシュを無効にするのはかなり難しいなどです。RSLの問題のいくつかかもしれません。これで問題は解決しましたが、Flash 6以降、Flashを使用してきましたが、RSLを使用するというアイデアをときどき楽しんでいました(アイデア自体が非常に重要であるため)。意味のある、実装はさておき)、次々に問題を見つけた後にそれを放棄するだけです。

この問題を(RSLをまったく使用せずに)回避するためのオプションは、art.swfをロードするshell.swfを使用し、ロードされると現在のコードをロードすることです。code.swfがロードされるまでに、art.swfはすでにロードされているため、ベリファイアはcom.foo.graphics.A(code.swfで)チェックするときにcom.foo.graphics.B(art.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したい場合は、プロキシでこれらの値を変更するだけで、実際には同じ結果になります。それが当てはまらない場合は、プロキシされたオブジェクト()に実際に作用するゲッター/セッターを追加できます。ywidthcom.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コンパイラーによってチェックされないことですが、それは1つの場所にしか存在しないため、管理しやすいと思います。そしてもちろん、アセットライブラリに依存するクラスをインスタンス化する前に、アセットライブラリがロードされていることを確認する必要があります。そうしないと、期待を裏切ることになります。しかしそれ以外は、これは私にとってかなりうまくいきました。

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());
    }

}

同じプロキシのアイデアですが、アプリケーションドメインを使用して文字列からオブジェクトをインスタンス化することを心配する必要はありません。

于 2010-06-26T20:31:42.353 に答える