1. ホーム
  2. swift

[解決済み] Swiftで失敗しないイニシャライザーを実装するためのベストプラクティス

2023-02-03 18:57:09

質問

以下のコードで、シンプルなモデルクラスと、(json-)辞書をパラメータとする失敗しないイニシャライザーを定義しようと思います。イニシャライザーは nil を返すようにします。

1. なぜコードがコンパイルされないのですか?エラーメッセージにはこうあります。

クラスインスタンスの保存されたプロパティは、イニシャライザからnilを返す前にすべて初期化する必要があります。

それは意味がありません。を返す予定なのに、なぜこれらのプロパティを初期化しなければならないのでしょうか? nil ?

2. 私のアプローチは正しいのでしょうか、それとも私の目標を達成するために他のアイデアや共通のパターンがあるのでしょうか?

class User: NSObject {

    let userName: String
    let isSuperUser: Bool = false
    let someDetails: [String]?

    init?(dictionary: NSDictionary) {
        if let value: String = dictionary["user_name"] as? String {
            userName = value
        }
        else {
           return nil
        }

        if let value: Bool = dictionary["super_user"] as? Bool {
            isSuperUser = value
        }

        someDetails = dictionary["some_details"] as? Array

        super.init()
    }
}

どのように解決するのですか?

更新しました。 より Swift 2.2 変更履歴 (2016年3月21日リリース)を参照してください。

failable または throwing と宣言された指定クラスイニシャライザーは、オブジェクトが完全に初期化される前に、それぞれ nil を返したり、エラーをスローしたりすることができるようになりました。


Swift 2.1 以前のバージョン用です。

Appleのドキュメント(とコンパイラーエラー)によると、クラスは、その保存されたプロパティをすべて初期化してから nil を返す前に、すべての保存されたプロパティを初期化しなければなりません。

しかし、クラスでは、フェイルイニシャライザは、そのクラスによって導入されたすべての保存されたプロパティの後にのみ、初期化の失敗を引き起こすことができます。 初期化の失敗を引き起こすことができるのは、そのクラスによって導入されたすべての保存されたプロパティが初期値に設定された後だけです。 そのクラスによって導入されたすべての保存されたプロパティが初期値に設定され、すべてのイニシャライザーの委譲が行われた後にのみ、初期化の失敗を引き起こすことができます。 の委譲が行われた後でのみ、初期化の失敗を引き起こすことができます。

注意 実際には、構造体や列挙についてはうまく動作しますが、クラスについてはそうではありません。

イニシャライザーが失敗する前に初期化できないストアドプロパティを処理するために提案された方法は、暗黙的にラップされていないオプショナルとしてそれらを宣言することです。

docsからの例です。

class Product {
    let name: String!
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}

上記の例では、Product クラスの name プロパティは は暗黙のうちにアンラップされたオプションの文字列型を持つものとして定義されています。 (String!)と定義されています。オプション型であるため、これは、name プロパティは、初期化時に特定の値が割り当てられる前に、デフォルト値として nil を持つことを意味します。 を持つことになります。このnilというデフォルト値は、つまり このnilというデフォルト値は、Productクラスが導入するすべてのプロパティが有効な初期値を持つことを意味します。 有効な初期値を持っていることを意味します。その結果、Product のフェイル可能なイニシャライザ のフェイル可能なイニシャライザは、イニシャライザの開始時に初期化の失敗を引き起こすことができます。 に特定の値を代入する前に、空文字列が渡された場合、初期化の失敗を引き起こすことができます。 イニシャライザー内の name プロパティに特定の値を割り当てる前に

しかし、あなたの場合、単に userNameString! としても、コンパイルエラーは修正されません。なぜなら、ベース・クラスのプロパティの初期化についてまだ心配する必要があるからです。 NSObject . 幸いなことに userName として定義されています。 String! として定義されている場合、実際に呼び出すことができるのは super.init() の前に return nil を開始します。 NSObject を初期化し、コンパイルエラーを修正します。

class User: NSObject {

    let userName: String!
    let isSuperUser: Bool = false
    let someDetails: [String]?

    init?(dictionary: NSDictionary) {
        super.init()

        if let value = dictionary["user_name"] as? String {
            self.userName = value
        }
        else {
            return nil
        }

        if let value: Bool = dictionary["super_user"] as? Bool {
            self.isSuperUser = value
        }

        self.someDetails = dictionary["some_details"] as? Array
    }
}