/** * * @see http://pierrechamberlain.ca/blog/2011/04/custom_metadata/ * 翻译力求准确,如有错误或不准确的,欢迎指出 * */
自从我涉足AS3以来,我从来不知道6年之后我还能在这门强大的语言里面发现新东西。
我想与大家分享一下我最近的发现,以及他如何潜在地为加快你的开发时间提供了新的门道。
首先:元数据标签,它们是啥?
嗯,如果你开发Flex几年了,你可能会偶然碰到这些内置的元数据标签。
//Specifies the properties of the SWF when it is launched. [SWF(backgroundColor="0x000033",width="800",height="600")] //Designates another class as the main-entry point, for //preloading this current one. [Frame(factoryClass="mx.managers.SystemManager")] //Compiles an external resource (image) into the SWF. [Embed(source="assets/images/background.jpg")] //Makes a property available for Flex data-bindings [Bindable] //Makes a property exposed in the IDE's Component inspector panel, //with the option to set certain range values. [Inspectable]
其中一些有一系列可以设置的属性(更多信息请参见这里的文档)。
在很长一段时间里,我认为这些元数据标签只有在编译时可用——所以我假设我们只能受限于Adobe(当然,也是MacroMedia)所提供的标签。
然而,实际上却不是这么回事。元数据标签在运行时也能使用。看下面的例子:
package { import flash.display.Sprite; import flash.utils.describeType; /** * An example of enumerating a class's Metadata information. * @author Pierre Chamberlain */ [CustomMeta] public class MetadataTest extends Sprite{ public function MetadataTest() { super(); var xDesc:XML = describeType(Object(this).constructor); var xMetas:XMLList = xDesc.factory.metadata; trace(xMetas); } } }
上述代码trace出了一下结果:
<metadata name="CustomMeta"/> <metadata name="__go_to_ctor_definition_help"> <arg key="file" value="C:\...\src\MetadataTest.as" /> <arg key="pos" value="214" /> </metadata> <metadata name="__go_to_definition_help"> <arg key="file" value="C:\...\src\MetadataTest.as" /> <arg key="pos" value="162" /> </metadata>
从输出中你可以看出有一对预制的元数据标签——“__go_to_ctor_definition_help”和“__go_to_definition_help”。写这的时候。在写这篇文章的时候,我只会认为它们为了让调试器跟踪给定类从那一行或者哪个字符开始。那只是我的猜想。
如果你想知道,你可以在实例本身调用describeType(this)方法,但现在我只想获取实例类的类型描述(Type Description on the Class of the instance instead)。产生的XML定义与类和实例版本的没什么太大的不同(Class vs.Instance),但你可能必须使用略微不同的方法来遍历E4X链来获得给定类相关的元数据标签(就像它也可能遍历它继承的元数据标签)。
你也会注意到-我们的自定义元数据标签在列表中被捕获到了。在这样的情景下,它是一个自封闭的XML元素,因为我们在元数据标签声明时没有提供任何参数。我们可以改一下那:
[CustomMeta(param1,param2)] /* Outputs: <metadata name="CustomMeta"> <arg key="" value="param1" /> <arg key="" value="param2" /> </metadata> */
如果看仔细点,我们会注意到元素的key属性没有任何数据。只有在小括号“()”之间插入纯文本时这才会发生。这并不一定错误,恰恰相反,它使得在你声明中插入Enabled/Disabled或者切换变得更加简单。在你使用元数据标签时,无需使用键值对来打开特定的布尔标签(enable certain boolean-flags),只需假定如果在参数列表中找到了这些关键字,那么这些属性就是可用的。
下一个列子展示了当我们为关键字赋值的结果
[CustomMeta(param1="foo",param2="bar")] /* Outputs: <metadata name="CustomMeta"> <arg key="param1" value="foo" /> <arg key="param2" value="bar" /> </metadata> */
如果与之前的输出相比较,我们可以注意到每对参数关键字都移到了key属性上,使得他们的value属性可以保存它们的数据。
成员标签:属性,方法,Getters & Setters
元数据标签并没有限定只有类级别才能够声明。Flex开发者肯定看过(或许很多次了!)在类的实例变量或者getter或setter上使用[Bindable].
如果我们将自定义元数据标签像[Bindable]那样做,他们可以通过像下面的代码一样访问。
package { import flash.display.Sprite; import flash.utils.describeType; /** * An example of enumerating a class's Metadata information. * @author Pierre Chamberlain */ [CustomMeta(param1="foo",param2="bar")] public class MetadataTest extends Sprite{ [MemberMetaExamplePrivate] private var _myPrivateVar:Boolean; [MemberMetaExamplePublic] public var myPublicVar:String; [MemberMetaExampleConstructor] public function MetadataTest() { super(); analyzeMetadata(); } private function analyzeMetadata():void{ var myClass:Class = Object(this).constructor; var xDesc:XML = describeType(myClass); var myClassName:String = xDesc.@name; var xMetas:XMLList = xDesc.factory..metadata; //Filter the Metadata Tags belonging to this Class only: var xMetaParent:XML; var metaParents:Array = []; for each(var xMeta:XML in xMetas) { xMetaParent = xMeta.parent(); if (xMeta.@name.indexOf("__go_to") > -1) { delete xMetaParent.children()[xMeta.childIndex()]; continue; } if (xMetaParent.name() == "factory") { metaParents.push(xMeta); continue; } var declaredBy:String = xMetaParent.attribute("declaredBy"); if (declaredBy && declaredBy != myClassName) { continue; } metaParents.push( xMetaParent ); } trace(metaParents.join("\n")); } [MemberMetaExampleMethod] public function myMethod():void { } [MemberMetaExampleGetter] public function get myAccessor():Boolean { return _myPrivateVar; } public function set myAccessor(value:Boolean):void { _myPrivateVar = value; } } }
/* Outputs: <variable name="myPublicVar" type="String"> <metadata name="MemberMetaExamplePublic" /> </variable> <accessor name="myAccessor" access="readwrite" type="Boolean" declaredBy="MetadataTest"> <metadata name="MemberMetaExampleGetter" /> </accessor> <method name="myMethod" declaredBy="MetadataTest" returnType="void"> <metadata name="MemberMetaExampleMethod" /> </method> <metadata name="CustomMeta"> <arg key="param1" value="foo" /> <arg key="param2" value="bar "/> </metadata> */
对于上面的xml过滤过程我不会深究,但你可以看看我们在输出结果中捕获的:CustomMeta,MemberMetaExampleMethod,MemberMetaExampleGetter和MemberMetaExamplePublic.我们可以得出一个结论:私有变量,方法,Getters和Setters以及类的构造函数不能应用元数据标签。
说了这么多,我们该怎么开始呢?我们怎样在AS3中使用自定义元数据标签来增强我们的开发?(待续。。。)
注:如果你碰到过这种情况,就是你的自定义标签不存在,那么你很可能编译的SWF的release版,release版默认会丢弃掉这些元数据标签。要保存这些元数据标签,在编译器参数中为每一个自定义标签指定“-keep-as3-metadata+=CustomMeta”(感谢Damiana Connolly指出这一点)。
自己注:在使用robotlegs时会用到-keep-as3-metadata+=inject的东西,因为robotLegs使用自定义的元数据标签