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

3

假设有以下构造函数:

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值将等于“var”。

通常,我如何在Swift中使用反射来获取参数变量的名称,就像这个例子一样?到目前为止,我已经查看了许多资源,从苹果的文档到运行时API和类似的资源,但不幸的是,没有一个预定义的运行时方法在Swift中适用。


请问您能展示一下您的ChildElement类/结构体吗? - dfrib
当然!请查看更新后的问题。虽然ChildElement类的结构与此问题无关。 - dsafa
对于所有问题来说,提供一个最小、完整且可验证的示例非常重要,因为这可以增加别人帮助你的可能性(有些人如果需要添加自定义的虚拟类来复现某个行为,他们可能就不会费力尝试了)。 - dfrib
2个回答

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"

0

我知道你只要求检索属性名称,但如果你也对某个类的属性类型感兴趣 - 而不需要该类的实例 - 我已经为此编写了一个项目。

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

我在StackOverflow这里写了一个详细的解释,我的项目可以在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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接