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

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

我需要在视图控制器中使用许多不同的单元格types(单元格组织在不同的nib文件中)来注册单元重用标识符。 所以我的想法是将所有的重用标识符和相应的nib文件作为静态元组属性(reuseID,nibName)在一个结构中。 但我怎么能遍历所有这些注册与tableView单元格?

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

虽然老问题,用Swift演变这个问题有了新的答案。 我认为你的方法是更好的描述情况,但原来的问题是如何遍历结构属性,所以这里是我的答案( 适用于类和结构

您可以使用“ 镜像结构参考” 。 重点是,调用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 _ { } 

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

使用hris.to真棒的答案,我想提供一个Swift 3的答案,更重要的是,不使用单身。

协议和扩展:

 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] 

知道在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)] 

下面是使用Swifts元组特性迭代结构属性(重用UITableViewCells的标识符和相应的NIB名称)的示例。 如果你喜欢在nib文件中组织你的单元格,并且有一个使用许多不同单元types的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循环很容易注册所有单元格types:

 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 👎") } }