不能从RSL中定义的类继承?

时间:2010-06-24 04:05:28

标签: flex flash actionscript-3 rsl

注意:这是一个Actionscript项目,而不是Flex项目。

我在RSL中定义了A类(从技术上讲,它是我在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 {}

1 个答案:

答案 0 :(得分: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是一个皇家的痛苦,不值得麻烦(只是为了说一些恼人的“功能”:你必须硬编码网址,在必要时很难使缓存失效等等。也许一些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中)。 / p>

    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方法,它应该可以正常工作。

但我不喜欢这种方法是你必须为shell创建另一个项目。

我所做的通常是有一个代表图形资产的类。

    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只是一个图形资产,因此您不需要代理其他内容。我的意思是,如果您想要更改xywidth等等,您只需更改代理中的这些值,结果实际上是相同的。如果在某些情况下不是这样,您可以添加实际作用于代理对象(com.foo.graphics.A)的getter / setter。

您可以将其抽象为基类:

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


}    

此外,将app域注入依赖项(并将实例化机制移动到其他实用程序类)对某些项目可能很有用,但上述代码在大多数情况下都很好。

现在,这种方法的唯一问题是编译器没有检查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());
    }

}

相同的代理理念,但您不必担心使用应用程序域从字符串中实例化对象。