如何在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 } }
所以现在我们可以用这个方法遍历任何 class
或struct
的属性。 我们只需将该类标记为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 👎") } }