Swiftで構造体のプロパティを反復することは可能ですか?
多くの異なるセルタイプを利用するビューコントローラーでセル再利用識別子を登録する必要があります(セルは異なるnibファイルに編成されています)。したがって、私の考えは、すべての再利用識別子と対応するnibファイルを静的タプルプロパティ(reuseID、nibName)として構造体に配置することでした。しかし、どのようにしてそれらすべてを反復して、セルをtableViewに登録できますか?
私はすでに何かを試しました(以下の私の答えを参照してください)。しかし、これを行うためのより簡単な方法があります。すべてのプロパティを配列に入れずに?
古い質問ですが、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
}
}
これで、このメソッドを使用してanyclass
またはstruct
のプロパティをループできます。クラスをPropertyLoopable
としてマークする必要があります。
(例のように)物事を静的に保つために、シングルトンも追加します。
struct ReuseID: PropertyLoopable {
static let instance: ReuseID = ReuseID()
}
シングルトンを使用するかどうかにかかわらず、最終的に次のようにプロパティをループできます。
do {
print(try ReuseID.instance.allProperties())
} catch _ {
}
そして、それはループ構造体のプロパティについての説明です。楽しむSwift;)
hris.toの素晴らしい答えを使用して、私はSwiftより適切な答えを提供したかったので、シングルトンを使用しません。
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
、Swift 3および4に対するhris.toの回答への追加です。
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 Tuple機能を使用して構造体プロパティ(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 ????")
}
}
オブジェクトであるプロパティをより深く掘り下げる@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
でないかどうかのチェックを廃止しました。これは、class
またはstruct
ではないパラメーターが再帰関数の基本ケースであり、エラーよりも手動で処理する方が簡単だからです。
それをテストする:
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]
(辞書は順序付けされていないため、別の順序で取得した場合はそのためです)
これを行うにははるかに簡単な方法があります:
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 MyStruct: Encodable {...}
class MyClass: Encodable {...}
そして、あなたはいつでもあなたの構造体/クラスインスタンスを表す辞書を取得することができます:
var a: MyStruct
var b: MyClass
print(a.dictionary)
print(b.dictionary)
そして、あなたはキーをループすることができます:
for (key, value) in a.dictionary { ... }
for (key, value) in b.dictionary { ... }