通过在Swift中订阅访问属性

通过在Swift中订阅访问属性,swift,swift-playground,Swift,Swift Playground,我在Swift中有一个自定义类,我想使用订阅来访问它的属性,这可能吗 我想要的是这样的东西: class User { var name: String var title: String subscript(key: String) -> String { // Something here return // Return the property that matches the key… } init(na

我在Swift中有一个自定义类,我想使用订阅来访问它的属性,这可能吗

我想要的是这样的东西:

class User {
    var name: String
    var title: String

    subscript(key: String) -> String {
        // Something here
        return // Return the property that matches the key…
    }

    init(name: String, title: String) {
        self.name = name
        self.title = title
    }
}

myUser = User(name: "Bob", title: "Superboss")
myUser["name"] // "Bob"
更新:我之所以要寻找它,是因为我使用HTML模板进行渲染。我希望能够将我的模型对象传递给GRMustache渲染器

GRMustach使用键控的订阅objectForKeyedSubscript:方法和键值编码valueForKey:方法获取值。任何兼容对象都可以为模板提供值


我想你可以做到:

class User {
    let properties = Dictionary<String,String>()

    subscript(key: String) -> String? {
        return properties[key]
    }

    init(name: String, title: String) {
        properties["name"] = name
        properties["title"] = title
    }
}
值得注意的是,Swift目前似乎不支持按名称进行反射。
reflect
函数返回一个
Mirror
,其下标是基于
Int
的,而不是基于
String

(此处为GRMustache作者)

在一个面向swift的Mustache库出现之前,我建议让您的类从NSObject继承(以便它们具有
valueForKey:
方法)。GrMustach然后将使用此方法获取值

如果这仍然不起作用(渲染中的空白值),您可以尝试禁用GRMustache安全功能(请参阅)

如果您遇到任何其他问题,请直接在存储库中打开一个问题:


2015年2月2日编辑:GRMustache.swift退出:

使用valueForKey应使您能够使用其名称访问属性。确保您正在使用继承NSObject的对象

class people: NSObject {
    var age: NSString = "44"
    var height: NSString = "153"
}

let person:people = people()

let stringVariable = "age"

person.valueForKey("age")
// Print "44"

person.valueForKey("\(stringVariable)")
// Print "44"

这是一个使用反射的小技巧。可以使用以下内容

protocol PropertyReflectable { }

extension PropertyReflectable {
    subscript(key: String) -> Any? {
        let m = Mirror(reflecting: self)
        for child in m.children {
            if child.label == key { return child.value }
        }
        return nil
    }
}

struct Person {
    let name: String
    let age: Int
}

extension Person : PropertyReflectable {}
然后创建一个
人物
,并访问其键控属性

let p = Person(name: "John Doe", age: 18)

p["name"] // gives "John Doe"
p["age"] // gives 18
let p = Person(name: "John Doe", age: 18)

p["name"] // gives "John Doe"
p["age"] // gives 18

您可以修改下标以始终返回属性值的插值字符串。

Shim的上述答案在Swift 4中不再有效。有两件事你应该注意

首先,如果要使用
value(forKey:)
函数,则类必须继承
NSObject

其次,由于Objective-C对值类型一无所知,您必须将
@objc
关键字放在您的值类型属性前面,Swift将为您完成繁重的工作

以下是一个例子:

import Foundation

class Person: NSObject {
    @objc var name: String = "John Dow"
    @objc var age: Int = 25
    @objc var height: Int = 180

    subscript(key: String) -> Any? {
        return self.value(forKey: key)
    }
}

let person: Person = Person()

person["name"] // "John Dow"
person["age"] // 25
person["height"] // 180

在Benzi的答案中添加一些语法糖:

protocol PropertyReflectable { }

extension PropertyReflectable {
    subscript(key: String) -> Any? {
        let m = Mirror(reflecting: self)
        return m.children.first { $0.label == key }?.value
    }
}

struct Person {
    let name: String
    let age: Int
}

extension Person : PropertyReflectable {}
然后创建一个
人物
,并访问其键控属性

let p = Person(name: "John Doe", age: 18)

p["name"] // gives "John Doe"
p["age"] // gives 18
let p = Person(name: "John Doe", age: 18)

p["name"] // gives "John Doe"
p["age"] // gives 18

属性不是以字典的形式存储的,所以我猜您不能像您希望的那样访问它们。
myUser.name
有什么问题吗?因为名称和标题不是私有的(因为现在还不可能),所以你不会从中受益。我更新了这个问题,解释了为什么我想要这样的东西。我回答这个问题是因为我认为它教你如何订阅,切换,也许有一点关于反射(或缺少反射)。也就是说,我不建议执行上述任何操作。对于Swift 3.0:
person.value(forKey:propertyName)
在获取该值之前如何检查该值是否存在?这非常完美。允许我通过字符串名称访问任何属性这是一个很好的解决方案,但应该注意,这(以及一般的反射)不适用于Swift中的计算属性