3

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

我在 RSL 中定义了 A 类(从技术上讲,它是我在 Flash IDE 中制作并导出以用于操作脚本的艺术资产。然后将整个 .FLA 导出为 SWC/SWF)。

我的主要项目有 B 类,它继承自 A 类。该项目编译良好,没有错误。

但是,当项目运行并尝试创建 B 类的实例时,出现验证错误。创建一个 Class 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 中(实际上,情况更糟,因为这样做只会增加应用程序的全局下载大小) .

所以,如果你将你的艺术库设置为外部,编译器可以进行类型检查,你将在你的 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 中)时找到(在 art.swfcom.foo.graphics.B中)。

    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只是一个图形资产,因此您实际上不需要代理其他东西。我的意思是,如果你想改变x, y, width, 等等,你可以在代理中改变这些值,结果实际上是一样的。如果在某些情况下不是这样,您可以添加一个实际作用于代理对象 ( 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");
    }


}    

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

现在,这种方法的唯一问题是B编译器不检查构造函数中的链接名称,但由于它只在一个地方,我认为它是可管理的。而且,当然,您应该确保在尝试实例化依赖于它的类之前加载您的资产库,否则它将产生预期。但除此之外,这对我来说效果很好。

附言

我刚刚意识到,在您当前的情况下,这实际上可能是一个更简单的解决方案:

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 回答