Swift反射功能 - 如何获取实例变量名称?

时间:2016-01-31 12:53:57

标签: swift reflection

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

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请求。

通过获取变量名称的能力(例如pTableName应生成" pTableName":String),我想完成以下任务:

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

然后对于数组的每个元素产生:

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

虽然ChildElement类的结构等于:

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中使用的那个:

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

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

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

然后当然返回的NSString值将等于&#34; var&#34;。

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

2 个答案:

答案 0 :(得分:1)

Swift的反射功能有限,使用起来有点麻烦,所以我做了几个实用功能来简化它们。这是基于Mirror类(现在只读)

另请注意,只能使用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"

答案 1 :(得分:0)

我知道你只要求检索属性的名称,但是如果你也对某个类中的属性类型感兴趣 - 不需要具有所述类的实例 - 我已经写了一个项目此

我有一个解决方案,可以找到任何继承自NSObject的类的属性的名称和类型。

我在StackOverflow here上写了一个冗长的解释,我的项目可用here on Github

简而言之,你可以做这样的事情(但实际上是检查代码Github):

public class func getTypesOfProperties(inClass clazz: NSObject.Type) -> Dictionary<String, Any>? {
    var count = UInt32()
    guard let properties = class_copyPropertyList(clazz, &count) else { return nil }
    var types: Dictionary<String, Any> = [:]
    for i in 0..<Int(count) {
        guard let property: objc_property_t = properties[i], let name = getNameOf(property: property) else { continue }
        let type = getTypeOf(property: property)
        types[name] = type
    }
    free(properties)
    return types
}