Access properties via subscripting in Swift

前提是你 提交于 2019-12-03 23:39:17

问题


I have a custom class in Swift and I'd like to use subscripting to access its properties, is this possible?

What I want is something like this:

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"

Update: The reason why I'm looking for this is that I'm using GRMustache to render from HTML templates. I'd like to be able to just pass my model object to the GRMustache renderer…

GRMustache fetches values with the keyed subscripting objectForKeyedSubscript: method and the Key-Value Coding valueForKey: method. Any compliant object can provide values to templates.

https://github.com/groue/GRMustache/blob/master/Guides/view_model.md#viewmodel-objects


回答1:


(GRMustache author here)

Until a swift-oriented Mustache library is out, I suggest having your classes inherit from NSObject (so that they have the valueForKey: method). GRMustache will then fetch values with this method.

In case this would still not work (blank values in the rendering), you may try to disable GRMustache security features (see https://github.com/groue/GRMustache/blob/master/Guides/security.md#disabling-safe-key-access)

Should you experience any other trouble, please open an issue right into the repository: https://github.com/groue/GRMustache/issues

EDIT February 2, 2015: GRMustache.swift is out: http://github.com/groue/GRMustache.swift




回答2:


This is a bit of a hack using reflection. Something along the lines of the following could be used.

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 {}

Then create a Person and access it's keyed properties.

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

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

You could modify the subscript to always return an interpolated string of the property value.




回答3:


Using valueForKey should enable you to access properties using their names. Be sure that you're working with a object that inherit 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"



回答4:


Adding some syntax sugar to Benzi's answer:

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 {}

Then create a Person and access it's keyed properties.

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

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



回答5:


Shim's answer above doesn't work anymore in Swift 4. There are two things you should be aware of.

First of all, if you want to use value(forKey:) function, your class must inherit NSObject.

Secondly, since Objective-C doesn't know anything about value type, you have to put the @objc keyword in front of your value type properties and Swift will do the heavy-lifting for you.

Here is the example:

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



回答6:


I suppose you could do:

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
    }
}

Without knowing your use case I would strongly advise against doing this.

Another approach:

class User {
    var name : String
    var title : String

    subscript(key: String) -> String? {
        switch key {
            case "name" : return name
            case "title" : return title
            default : return nil
        }
    }

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

It might be worth noting that Swift doesn't appear to currently support reflection by names. The reflect function returns a Mirror whose subscript is Int based, not String based.



来源:https://stackoverflow.com/questions/24138705/access-properties-via-subscripting-in-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!