web-dev-qa-db-ja.com

Xcode9とXcode10は、同じSwiftバージョンでも、異なる結果をもたらします

私はこのコードをxcode9.3とxcode10 beta3プレイグラウンドの両方で実行しています

import Foundation

public protocol EnumCollection: Hashable {
    static func cases() -> AnySequence<Self>
}

public extension EnumCollection {

    public static func cases() -> AnySequence<Self> {
        return AnySequence { () -> AnyIterator<Self> in
            var raw = 0
            return AnyIterator {
                let current: Self = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: self, capacity: 1) { $0.pointee } }

                guard current.hashValue == raw else {
                    return nil
                }

                raw += 1
                return current
            }
        }
    }
}

enum NumberEnum: EnumCollection{
    case one, two, three, four
}

Array(NumberEnum.cases()).count

両方ともSwift 4.1を使用していますが、

on xcode 9.配列のサイズは4

そしてxcode 10 beta配列のサイズは

私はこれをまったく理解していません。

10
kr15hna

これは、すべての列挙値のシーケンスを取得するための文書化されていない方法であり、以前のSwiftバージョンで偶然にのみ機能しました。これは、ゼロから始まる連続した整数である列挙値のハッシュ値に依存します。

Swift 4.2(Swift 4互換モードで実行している場合でも))では、ハッシュ値が常にランダム化されるため、これは間違いなく機能しなくなります。 を参照してください。 SE-0206ハッシュ可能な拡張機能

ハッシュ値の予測を困難にするために、標準のハッシュ関数はデフォルトで実行ごとのランダムシードを使用します。

あなたはそれを確認することができます

print(NumberEnum.one.hashValue)
print(NumberEnum.two.hashValue)

これはnotprint 0および1 Xcode 10を使用しますが、プログラムの実行ごとに異なる他の値もあります。

適切なSwift 4.2/Xcode 10ソリューションについては、 文字列型で列挙型を列挙する方法? :を参照してください。

extension NumberEnum: CaseIterable  { }
print(Array(NumberEnum.allCases).count) // 4
18
Martin R

これに対する解決策は、Xcode10およびSwift 4.2以降の場合です。

ステップ1:プロトコルEnumIterableを作成します。

protocol EnumIterable: RawRepresentable, CaseIterable {
    var indexValue: Int { get }
}

extension EnumIterable where Self.RawValue: Equatable {
    var indexValue: Int {
        var index = -1
        let cases = Self.allCases as? [Self] ?? []
        for (caseIndex, caseItem) in cases.enumerated() {
            if caseItem.rawValue == self.rawValue {
                index = caseIndex
                break
            }
        }
        return index
    }
}

ステップ2: EnumIteratorプロトコルを列挙型に拡張します。

enum Colors: String, EnumIterable {
    case red = "Red"
    case yellow = "Yellow"
    case blue = "Blue"
    case green = "Green"
}

ステップ3: hashValueを使用するのと同じようにindexValueプロパティを使用します。

Colors.red.indexValue
Colors.yellow.indexValue
Colors.blue.indexValue
Colors.green.indexValue

サンプルの印刷ステートメントと出力

print("Index Value: \(Colors.red.indexValue), Raw Value: \(Colors.red.rawValue), Hash Value: \(Colors.red.hashValue)")

出力: "インデックス値:0、生の値:赤、ハッシュ値:1593214705812839748"

print("Index Value: \(Colors.yellow.indexValue), Raw Value: \(Colors.yellow.rawValue), Hash Value: \(Colors.yellow.hashValue)")

出力:「インデックス値:1、生の値:黄色、ハッシュ値:-6836447220368660818」

print("Index Value: \(Colors.blue.indexValue), Raw Value: \(Colors.blue.rawValue), Hash Value: \(Colors.blue.hashValue)")

出力:「インデックス値:2、生の値:青、ハッシュ値:-8548080225654293616」

print("Index Value: \(Colors.green.indexValue), Raw Value: \(Colors.green.rawValue), Hash Value: \(Colors.green.hashValue)") 

出力:「インデックス値:3、生の値:緑、ハッシュ値:6055121617320138804」

2
Amit Gajjar

列挙型のhashValueを使用してケース値(位置またはID)を決定する場合、連続するint値0,1,2を返すことが保証されないため、これは間違ったアプローチです... Swift 4.2

たとえば、次のような列挙型を使用する場合:

enum AlertResultType {
    case ok, cancel 
}

この列挙型のhashValueは、0(ok)および1(キャンセル)の代わりに大きなint値を返す場合があります。

したがって、列挙型をより正確に宣言し、rowValueを使用する必要がある場合があります。例えば

enum AlertResultType : Int {
    case ok = 0, cancel = 1
}
0
Abhijith