web-dev-qa-db-ja.com

Swiftの構造体プロパティをループする方法は?

Swiftで構造体のプロパティを反復することは可能ですか?

多くの異なるセルタイプを利用するビューコントローラーでセル再利用識別子を登録する必要があります(セルは異なるnibファイルに編成されています)。したがって、私の考えは、すべての再利用識別子と対応するnibファイルを静的タプルプロパティ(reuseID、nibName)として構造体に配置することでした。しかし、どのようにしてそれらすべてを反復して、セルをtableViewに登録できますか?

私はすでに何かを試しました(以下の私の答えを参照してください)。しかし、これを行うためのより簡単な方法があります。すべてのプロパティを配列に入れずに?

28
blackjacx

古い質問ですが、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;)

21
hris.to

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]
9
John R Perry

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)]
3
Cœur

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 ????")
    }
}
2
blackjacx

オブジェクトであるプロパティをより深く掘り下げる@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
RPatel99

これを行うにははるかに簡単な方法があります:

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 { ... }
1
Frane Poljak