如何在Swift中循环结构属性?

时间:2014-12-04 10:57:21

标签: ios uitableview swift ios8 tuples

是否有可能在Swift中迭代结构的属性?

我需要在视图控制器中注册单元重用标识符,该控制器使用许多不同的单元格类型(单元格组织在不同的nib文件中)。所以我的想法是将所有重用标识符和相应的nib文件作为静态元组属性(reuseID,nibName)放在结构中。但是,如何迭代所有这些以使用tableView注册单元格?

我已经尝试了一些东西(请参阅下面的答案)。但有没有更简单的方法来做到这一点,例如不将每个属性都放在数组中?

6 个答案:

答案 0 :(得分:20)

虽然老问题,随着Swift的发展,这个问题有了新的答案。我认为你所采用的方法对于描述的情况更好,但原始问题是如何迭代结构属性,所以这是我的答案(适用于类和结构

您可以使用 Mirror Structure Reference 。重点是,在将reflect调用到某个对象后,你会得到它的“镜像”,这是非常谨慎但仍然有用的反射。

因此,我们可以轻松声明以下协议,其中key是属性的名称,value是实际值:

protocol PropertyLoopable
{
    func allProperties() throws -> [String: Any]
}

当然,我们应该使用新的协议扩展来为该协议提供默认实现:

extension PropertyLoopable
{
    func allProperties() throws -> [String: Any] {

        var result: [String: Any] = [:]

        let mirror = Mirror(reflecting: self)

        guard let style = mirror.displayStyle where style == .Struct || style == .Class else {
            //throw some error
            throw NSError(domain: "hris.to", code: 777, userInfo: nil)
        }

        for (labelMaybe, valueMaybe) in mirror.children {
            guard let label = labelMaybe else {
                continue
            }

            result[label] = valueMaybe
        }

        return result
    }
}

现在我们可以使用此方法循环遍历任何 classstruct的属性。我们只需将该类标记为PropertyLoopable

为了保持静态(如示例所示),我还将添加一个单例:

struct ReuseID: PropertyLoopable {
    static let instance: ReuseID = ReuseID()
}

无论是否使用单例,我们最终都可以遍历以下属性:

do {
    print(try ReuseID.instance.allProperties())
} catch _ {

}

这就是循环结构属性。快乐享受;)

答案 1 :(得分:9)

使用 hris.to 的精彩答案,我想提供一个 Swift 3 的答案,这个答案更加重要,并且没有&#39 ; t使用单身人士。

协议&扩展

protocol Loopable {
    func allProperties() throws -> [String: Any]
}

extension Loopable {
    func allProperties() throws -> [String: Any] {

        var result: [String: Any] = [:]

        let mirror = Mirror(reflecting: self)

        // Optional check to make sure we're iterating over a struct or class
        guard let style = mirror.displayStyle, style == .struct || style == .class else {
            throw NSError()
        }

        for (property, value) in mirror.children {
            guard let property = property else {
                continue
            }

            result[property] = value
        }

        return result
    }
}

实施例

struct Person: Loopable {
    var name: String
    var age: Int
}

var bob = Person(name: "bob", age: 20)

print(try bob.allProperties())

// prints: ["name": "bob", "age": 20]

答案 2 :(得分:3)

知道在Swift 1.2中你可以使用reflect(),因为Swift 2你可以使用Mirror,这里是 hris.to 的补充。回答Swift 3和4。

protocol Loopable {
    var allProperties: [String: Any] { get }
}
extension Loopable {
    var allProperties: [String: Any] {
        var result = [String: Any]()
        Mirror(reflecting: self).children.forEach { child in
            if let property = child.label {
                result[property] = child.value
            }
        }
        return result
    }
}

在任何结构或类上的用法:

extension NSString: Loopable {}

print("hello".allProperties)
// ["_core": Swift._StringCore(_baseAddress: Optional(0x00000001157ee000), _countAndFlags: 5, _owner: nil)]

答案 3 :(得分:2)

这是使用Swifts元组功能迭代结构属性(重用UITableViewCells的标识符和相应的NIB名称)的示例。如果你喜欢在nib文件中组织你的单元格并且有一个使用许多不同单元格类型的UIViewController,这很有用。

struct ReuseID {
  static let prepaidRechargeCreditCell = "PrepaidRechargeCreditCell"
  static let threeTitledIconCell = "ThreeTitledIconCell"
  static let usageCell = "UsageCell"
  static let detailsCell = "DetailsCell"
  static let phoneNumberCell = "PhoneNumberCell"

  static let nibNamePrepaidRechargeCreditCell = "PrepaidRechargeCreditCell"
  static let nibNameThreeTitledIconCell = "IconCellWith3Titles"
  static let nibNameUsageCell = "ListElementRingViewCell"
  static let nibNameDetailsCell = "ListElementStandardViewCell"
  static let nibNamePhoneNumberCell = "PhoneNumberCell"

  static let allValuesAndNibNames = [
    (ReuseID.prepaidRechargeCreditCell, ReuseID.nibNamePrepaidRechargeCreditCell),          
    (ReuseID.threeTitledIconCell, ReuseID.nibNameThreeTitledIconCell), 
    (ReuseID.usageCell, ReuseID.nibNameUsageCell), 
    (ReuseID.detailsCell, ReuseID.nibNameDetailsCell), 
    (ReuseID.phoneNumberCell, ReuseID.nibNamePhoneNumberCell)]
}

使用该结构,可以使用for循环轻松注册所有单元格类型:

for (reuseID, nibName) in ReuseID.allValuesAndNibNames {
    if let xibPath = NSBundle.mainBundle().pathForResource(nibName, ofType: "nib") {
        let fileName = xibPath.lastPathComponent.stringByDeletingPathExtension
        self.tableView.registerNib(UINib(nibName: fileName, bundle: nil), forCellReuseIdentifier: reuseID)

    } else {
        assertionFailure("Didn't find prepaidRechargeCreditCell ")
    }
}

答案 4 :(得分:1)

现在有一种更简单的方法:

1:创建一个可编码协议扩展名:

extension Encodable {
    var dictionary: [String: Any]? {
        guard let data = try? JSONEncoder().encode(self) else { return nil }
        return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] }
    }
}

2:使您的struct / class符合可编码协议

struct MyStruct: Encodable {...}
class MyClass: Encodable {...}

然后您可以随时获取一个表示您的struct / class实例的Dictionary:

var a: MyStruct
var b: MyClass

print(a.dictionary)
print(b.dictionary)

然后您可以循环浏览按键:

for (key, value) in a.dictionary { ... }
for (key, value) in b.dictionary { ... }

答案 5 :(得分:0)

我基于@John R Perry的解决方案创建了一个递归函数,该函数更深入到对象的属性中。它还需要一个参数来限制其深度(默认为Int.max),以防止堆栈溢出:

protocol Loopable {
    func allProperties(limit: Int) [String: Any]
}

extension Loopable {
    func allProperties(limit: Int = Int.max) [String: Any] {
        return props(obj: self, count: 0, limit: limit)
    }

    private func props(obj: Any, count: Int, limit: Int) -> [String: Any] {
        let mirror = Mirror(reflecting: obj)
        var result: [String: Any] = [:]
        for (prop, val) in mirror.children {
            guard let prop = prop else { continue }
            if limit == count {
                result[prop] = val
            } else {
                let subResult = props(obj: val, count: count + 1, limit: limit)
                result[prop] = subResult.count == 0 ? val : subResult
            }
        }
        return result
    }
}

我摆脱了检查对象是class还是struct的过程,因为参数不是classstruct是对象的基本情况递归函数,并且手动处理比出现错误要容易。

测试:

class C {
    var w = 14
}
class B: Loopable {
    var x = 12
    var y = "BHello"
    var z = C()
    static func test() -> String {
        return "Test"
    }
}
class A: Loopable {
    var a = 1
    var c = 10.0
    var d = "AHello"
    var e = true
    var f = B()
    var g = [1,2,3,4]
    var h: [String: Any] = ["A": 0, "B": "Dictionary"]
    var i: Int?
}
print(A().allProperties())

打印:

["e": true, "g": [1, 2, 3, 4], "f": ["z": ["w": 14], "x": 12, "y": "BHello"], "h": ["A": 0, "B": "Dictionary"], "c": 10.0, "i": nil, "d": "AHello", "a": 1]

(词典是无序的,所以如果您获得不同的顺序,这就是为什么)