无法从 RSL 中定义的类继承?

发布于 2024-09-06 12:18:21 字数 1034 浏览 2 评论 0原文

注意:这是一个 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 {}

Note: This is an Actionscript project, not a Flex project.

I have class A defined in an RSL (technically, it's an art asset that I made in the Flash IDE and exported for actionscript. The entire .FLA was then exported as a SWC/SWF).

I my main project I have class B, which inherits from class A. The project compiles fine, with no errors.

However, when when the project runs and I try to create an instance of Class B, I get a verify error. Creating an instance of Class A works just fine, however:

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.

For reference, here is how I'm loading the 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);

Class B is defined as follows:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

清风挽心 2024-09-13 12:18:21

我不认为这是一个错误。这更多的是一个链接问题。

当您尝试创建 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 正在被定义。因此,在运行时,每当代码中首次引用 B 时,都必须定义 A。否则,验证者会发现不一致并崩溃。

在 Flash IDE 中,当您链接符号时,会出现“在第一帧中导出”选项。这是默认情况下导出代码的方式,但这也意味着可以推迟玩家首次引用类的定义。 Flex 使用它来预加载。它只加载一小部分 swf,足以显示预加载器动画,同时加载其余代码(不是“在第一帧中导出”)和资源。至少可以说,手动执行此操作似乎有点麻烦。

理论上,如果我正确地记得 RSL 是如何工作的,那么使用 RSL 应该会有所帮助(这个想法是 RSL 应该由播放器透明地加载)。根据我的经验,RSL 是一种巨大的痛苦,不值得麻烦(仅举几个烦人的“功能”:您必须对 URL 进行硬编码,在必要时使缓存失效相当困难,等等。也许是 RSL 的一些问题已经过去了,现在一切都正常了,但我可以告诉你,自从 Flash 6 以来,我一直在使用 Flash,多年来,我时不时地会考虑使用 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。方法,并且应该可以正常工作。

但我不喜欢这种方法的是您必须为 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");
    }


}    

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

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

}

相同的代理思想,但您无需担心使用字符串实例化对象应用领域。

I don't think this is a bug. It's more a linkage problem.

The verifier error doesn't happen when you try to create an instance of B. It happens as soon as your main swf is loaded and verified by the player. This is an important distinction. To see what I mean, change this code:

var bar:B = new B(); 

to

var bar:B;

You'll still get the error.

I don't know how you are builing the swf, but from the error it seems evident that the A class (B's parent) is being excluded from the swf. I can reproduce this using this mxmlc switch:

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

However, changing it to:

-compiler.library-path "lib.swc"

The problem goes. Obviously, this kind of defeats the purpose of loading the assets at runtime, since these assets are already compiled into your main.swf (in fact, it's worse, because by doing that you've just increased the global download size of your app).

So, if you set your art lib as external, the compiler can do type checking, you'll get auto-complete in your IDE, etc. Your B class still depends on A being defined, though. So, at runtime, A has to be defined whenever B is first referenced in your code. Otherwise, the verifier will find an inconsitency and blow up.

In the Flash IDE, when you link a symbol, there's a "export in first frame" option. This is how your code is exported by default, but it also means it's possible to defer when the definition of a class is first referenced by the player. Flex uses this for preloading. It only loads a tiny bit of the swf, enough to show a preloader animation while the rest of the code (which is not "exported in first frame") and assets are loaded. Doing this by hand seems a bit cumbersome, to say the least.

In theory, using RSL should help here if I recall correctly how RSL works (the idea being the a RSL should be loaded by the player transparently). In my experience, RSL is a royal pain and not worth the hassle (just to name a few annoying "features": you have to hard-code urls, it's rather hard to invalidate caches when necessary, etc. Maybe some of the RSL problems have gone and the thing works reasonably now, but I can tell you I've been working with Flash since Flash 6 and over the years, from time to time I'd entertain the idea of using RSL (because the idea itself makes a lot of sense, implementation aside), only to abandon it after finding one problem after the other.

An option to avoid this problem (without using RSL at all) could be having a shell.swf that loads the art.swf and once it's loaded, loads your current code. Since by the time your code.swf is loaded, art.swf has been already loaded, the verifier will find com.foo.graphics.A (in art.swf) when it checks com.foo.graphics.B (in 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);

    }

In your current main class, add this code:

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

Move your constructor logic (if any) to the init method, and it should work fine.

But what I don't like about this approach is that you have to create another project for the shell.

What I do, generally, is have a class that proxies the graphic asset.

    private var _symbol:MovieClip;

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

Since com.foo.graphics.A is just a graphical asset, you don't really need to proxy other stuff. What I mean is, if you want to change x, y, width, etc, etc, you can just change these values in the proxy and the result is in practice the same. If in some case that's not true, you can add a getter / setter that actually acts upon the proxied object (com.foo.graphics.A).

You could abstract this into a base class:

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


}    

Also, injecting the app domain as a dependency (and moving the instantiation mechanism to other utility class) could be useful for some projects, but the above code is fine in most situations.

Now, the only problem with this approach is that the linkage name in the constructor of B is not checked by the compiler, but since it's only in one place, I think it's manageable. And, of course, you should make sure your assets library is loaded before you try to instantiate a class that depends on it or it will trhow an expection. But other than that, this has worked fairly well for me.

PS

I've just realized that in your current scenario this could actually be a simpler solution:

public class B extends MovieClip {

    private var _symbol:MovieClip;

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

}

Or just:

public class B extends MovieClip {

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

}

The same proxy idea, but you don't need to worry about instantiating the object from a string using the application domain.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文