2016-01-31 1 views
1

Учитывая конструктор, такие как:Возможности быстрого отражения - как получить имя переменной экземпляра?

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 и добавьте их в список параметров, который впоследствии представляет собой запрос 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] 

Если я пытаюсь использовать макрос через объективный метод C, а затем применяется в Swift:

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

то, конечно, возвращаемое значение NSString будет равно «var».

В общем, как я могу использовать отражение в Swift, чтобы получить, как в этом примере, имя переменной аргумента? До сих пор я проверил множество ресурсов, начиная с документации Apple и заканчивая API-интерфейсом и аналогичным, но, к сожалению, ни один из предопределенных методов времени исполнения не применим в Swift.

+0

могли бы вы покажите нам свой 'ChildElement' класс/struct. – dfri

+0

Конечно!Проверьте обновленный вопрос. Хотя структура класса ChildElement не относится к этому вопросу. – dsafa

+0

Для всех вопросов важно представить пример [минимальный, полный и проверяемый] (http://stackoverflow.com/help/mcve), в основном для увеличения вероятности того, что кто-то поможет вам (поскольку некоторые не будут пытаться попробовать если им нужно добавить собственные собственные фиктивные классы, чтобы воспроизвести поведение, которое кто-то пытается опробовать). – dfri

ответ

1

Функции отражения Swift ограничены и немного громоздки для использования, поэтому я сделал несколько функций утилиты, чтобы упростить их. Это основано на классе 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 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 
}