Swift 反射功能 - 如何获取实例变量名称? [英] Swift reflection capabilities - how to get an instance variable name?

查看:64
本文介绍了Swift 反射功能 - 如何获取实例变量名称?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给定一个构造函数,例如:

Given a constructor such as:

required init (pTableName : String, pRecordKey : String, pStartAtRecord : Int){
    parameters.append(ChildElement(identifier: "pTableName", value: pTableName))
    parameters.append(ChildElement(identifier: "pRecordKey", value: pRecordKey))
    parameters.append(ChildElement(identifier: "pStartAtRecord", value: pStartAtRecord))
}

我想提取所有参数变量的名称,例如 pTableName、pRecordKey 等.为什么?首先,我可能有超过 30 个不同的类,它们具有不同的构造函数参数并将它们传递到参数数组中.其次,所有构造函数都有不同的签名,但它们主要做相同的事情——创建 ChildElements 并将它们附加到参数列表中——然后构成对 Web 服务的 SOAP 请求.

I want to extract the names of all argument variables such as pTableName, pRecordKey and so forth. Why exactly? First of all, I might have more then 30 different classes having different constructor arguments and passing those into the parameters array. Secondly, all of the constructors have a different signature, but mainly they do the same thing - create ChildElements and append them to the list of parameters - which afterwards constitutes a SOAP request for the web service.

通过具有获取变量名称的能力(例如 pTableName 应该产生 "pTableName" : String),我想完成以下操作:

By having the ability of getting the variable names (e.g. pTableName should produce "pTableName" : String), I would like to accomplish the following:

required init (pTableName : String, pRecordKey : String, pStartAtRecord : Int){
    appendToParameters([pTableName, pRecordKey, pStartAtRecord])
} 

然后会为数组的每个元素产生:

which would then for each element of the array produce:

parameters.append(ChildElement(identifier: Reflection.getName(var), value : var))

虽然 ChildElement 类的结构等于:

While the structure of the ChildElement class is equal to:

class ChildElement : Element {

var attributes : [String : String] = [:]
var identifier : String
var value : AnyObject

var toString : String {
    return "<\(identifier) \(self.concatinateAttributesAsString())>\(value)</\(identifier)>"
}

required init (identifier : String, value : AnyObject) {
    self.identifier = identifier
    self.value = value
}

但是,在 Swift 中,不可能使用反射和宏(例如 Objective-C 中使用的宏)来获取变量名称:

However, in Swift there is no possibility of getting the variable name using reflection and macros such as the one used in Objective-C:

#define variableName(var) [NSString stringWithFormat:@"%s",#var]

如果我尝试通过 Objective C 方法使用宏,然后在 Swift 中应用:

If I try to use the macro via an Objective C method, and then applied in Swift:

-(NSString *)getVarName:(id)var {
    return variableName(var)
}

当然,返回的 NSString 值将等于var".

then of course the return NSString value will be equal to "var".

一般来说,如何在 Swift 中使用反射来获取参数变量的名称,如本例所示?到目前为止,我已经检查了大量资源,从 Apple 的文档开始到运行时 API 和类似的,但不幸的是,预定义的运行时方法似乎都不适用于 Swift.

In general, how can I use reflection in Swift in order to obtain, as in this example the name of a argument variable? So far, I've checked numerous resources, starting from the Apple's documentation onto the runtime API and similar, but unfortunately, none of the pre-defined runtime methods seem to be applicable in Swift.

推荐答案

Swift 的反射特性有限,使用起来有点麻烦,所以我做了几个实用函数来简化它们.这是基于 Mirror 类(目前只读)

Swift's reflection features are limited and a bit cumbersome to use so I made a couple of utility functions to simplify them. This is based on the Mirror class (which is read only for now)

另请注意,使用镜像只能访问存储的属性.

Also note that only stored properties can be accessed using Mirror.

func fieldValue(object:AnyObject, _ name:String) -> Any?
{
   let nameComponents   = name.componentsSeparatedByString(".")

   guard let fieldName  = nameComponents.first 
   else { return nil }

   let subFieldName     = nameComponents.dropFirst().joinWithSeparator(".")

   for prop in Mirror(reflecting:object).children
   {
      if let name = prop.label
         where name == fieldName 
      {
         if subFieldName == "" { return prop.value }
         if let subObject = prop.value as? AnyObject
         { return fieldValue(subObject, subFieldName) } 
         return nil           
      }          
   }
   return nil
} 

func fieldNames(object:AnyObject, prefix:String = "") -> [String]
{
   var names:[String] = []
   for prop in Mirror(reflecting:object).children
   {
      if let name = prop.label
      {
         let fieldName = prefix + name
         names.append(fieldName)
         if let subObject = prop.value as? AnyObject
         { names = names + fieldNames(subObject, prefix:fieldName + ".") } 
      }          
   }
   return names
}


class ChildClass
{
   var subProperty:String = "ABC"
}

class ContainerClass
{
   var aProperty:Int       = 123
   var anObject:ChildClass = ChildClass()
}

let foo = ContainerClass()
fieldNames(foo)                        // ["aProperty", "anObject", "anObject.subProperty"]
fieldValue(foo,"aProperty")            // 123
fieldValue(foo,"anObject.subProperty") // "ABC"

这篇关于Swift 反射功能 - 如何获取实例变量名称?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆