迅速で明示的な例での便利なinitとinitの違いは何ですか?


82

両方の違い、またはの目的を理解するのに苦労していconvenience initます。


The Swift Programming Languageの本の初期化セクションを読んだことがありますか?あなたの混乱は正確には何ですか?
rmaddy 2016年

1
SOにはすでに回答があります。参照してください- stackoverflow.com/questions/30896231/...
Santosh

回答:


105

標準init

指定初期化子は、クラスの主要な初期化子です。指定された初期化子は、そのクラスによって導入されたすべてのプロパティを完全に初期化し、適切なスーパークラス初期化子を呼び出して、スーパークラスチェーンの上流で初期化プロセスを続行します。

convenience init

コンビニエンスイニシャライザはセカンダリであり、クラスのイニシャライザをサポートします。コンビニエンス初期化子を定義して、コンビニエンス・イニシャライザーと同じクラスから指定されたイニシャライザーを呼び出し、指定されたイニシャライザーのパラメーターの一部をデフォルト値に設定できます。便利な初期化子を定義して、特定のユースケースまたは入力値タイプ用にそのクラスのインスタンスを作成することもできます。

あたりスウィフトのドキュメント

簡単に言うと、これは、便利な初期化子を使用して、指定された初期化子の呼び出しをより速く、より「便利」にすることができることを意味します。したがって、便利な初期化子では、指定された初期化子のオーバーライドに表示されるself.init代わりに、super.initを使用する必要があります。

擬似コードの例:

init(param1, param2, param3, ... , paramN) {
     // code
}

// can call this initializer and only enter one parameter,
// set the rest as defaults
convenience init(myParamN) {
     self.init(defaultParam1, defaultParam2, defaultParam3, ... , myParamN)
}

これらは、主にデフォルトの長い初期化子を持つカスタムビューなどを作成するときによく使用します。ドキュメントは私ができるよりも説明するのに良い仕事をしています、それらをチェックしてください!


73

コンビニエンスイニシャライザーは、多くのプロパティを持つクラスがある場合に使用され、常にすべての変数でウィットを初期化するのは「苦痛」のようなものです。したがって、コンビニエンスイニシャライザーで行うことは、変数の一部を渡してオブジェクトを作成し、残りをデフォルト値で割り当てます。Ray WenderlichのWebサイトに非常に優れたビデオがありますが、有料アカウントを持っているため、無料かどうかはわかりません。これは、これらすべての変数でオブジェクトを初期化する代わりに、タイトルを付けるだけであることがわかる例です。

struct Scene {
  var minutes = 0
}

class Movie {
  var title: String
  var author: String
  var date: Int
  var scenes: [Scene]

  init(title: String, author: String, date: Int) {
    self.title = title
    self.author = author
    self.date = date
    scenes = [Scene]()
  }

  convenience init(title:String) {
    self.init(title:title, author: "Unknown", date:2016)
  }

  func addPage(page: Scene) {
    scenes.append(page)
  }
}


var myMovie = Movie(title: "my title") // Using convenicence initializer
var otherMovie = Movie(title: "My Title", author: "My Author", date: 12) // Using a long normal initializer

4
良い例え。これにより、概念が明確になりました。ありがとうございました。
Arjun Kalidas 2018

5
また、作成者のデフォルト値と日付パラメータを `init(title:String、author:String =" Unknown "、date:Int = 2016){self.title = title self.author = author selfとしてinit()を設定することもできます。 date = date scenes = [Scene]()} `では、なぜ便利な初期化子が必要なのですか?
shripad20 2018

@ shripad20同じ質問がここにあります。別の初期化子を作成し、そこからデフォルトのパラメーターを送信することはできますが、便利さを使用する必要がある理由を見つけましたか。答えが見つかったら教えてください
user100 2018

利便性がある追加のデフォルトパラメータを持つself.initがある一方で、「メイン」self.initに置き換え、「メイン」self.initため。 hackingwithswift.com/example-code/language/...
user1105951

@ shripad20は、便利な初期化子がデフォルト値を上回っている場所を明確にするために、以下の私の例を確認しください。それが役に立てば幸い。
クリスグラフ

14

これは、AppleDeveloperポータルから抜粋した簡単な例です。

基本的に、指定された初期化子はですinit(name: String)。これにより、格納されているすべてのプロパティが確実に初期化されます。

init()コンビニエンス初期化子は、引数を取らない、自動に設定の値nameに保存されたプロパティを[Unnamed]指定イニシャライザを使用することによって。

class Food {
    let name: String

    // MARK: - designated initializer
    init(name: String) {
        self.name = name
    }

    // MARK: - convenience initializer
    convenience init() {
        self.init(name: "[Unnamed]")
    }
}

// MARK: - Examples
let food = Food(name: "Cheese") // name will be "Cheese"
let food = Food()               // name will be "[Unnamed]"

少なくともいくつかのプロパティが保存されている大規模なクラスを扱う場合に便利です。AppleDeveloperポータルでオプションと継承についてもう少し読むことをお勧めします。


6

便利な初期化子がデフォルトのパラメータ値の設定に勝る場合

convenience initializersにとっては、クラスプロパティのデフォルト値を設定するだけでなく、やるべきことがたくさんある場合に役立ちます。

指定されたinit()を使用したクラスの実装

それ以外の場合は、init定義にデフォルト値を設定するだけです。例:

class Animal {

    var race: String // enum might be better but I am using string for simplicity
    var name: String
    var legCount: Int

    init(race: String = "Dog", name: String, legCount: Int = 4) {
        self.race = race
        self.name = name
        self.legCount = legCount // will be 4 by default
    }
}

便利なinit()によるクラス拡張

ただし、単にデフォルト値を設定するだけでなく、やるべきことがたくさんあるかもしれません。そのconvenience initializersために便利です。

extension Animal {
    convenience init(race: String, name: String) {
        var legs: Int

        if race == "Dog" {
            legs = 4
        } else if race == "Spider" {
            legs = 8
        } else {
            fatalError("Race \(race) needs to be implemented!!")
        }

        // will initialize legCount automatically with correct number of legs if race is implemented
        self.init(race: race, name: name, legCount: legs)
    }
}

使用例

// default init with all default values used
let myFirstDog = Animal(name: "Bello")

// convenience init for Spider as race
let mySpider = Animal(race: "Spider", name: "Itzy")

// default init with all parameters set by user
let myOctopus = Animal(race: "Octopus", name: "Octocat", legCount: 16)

// convenience init with Fatal error: Race AlienSpecies needs to be implemented!!
let myFault = Animal(race: "AlienSpecies", name: "HelloEarth")

1
よく、簡単なはここで説明
ビビ

4

便利な初期化子は、クラス拡張で定義できます。しかし、標準的なもの-できません。


1
質問に対する完全な答えではありませんが、これは私がまだ考えていなかったものです、ありがとう!
マルク=アンドレ・Weibezahn

3

convenience initそれが値でクラスを初期化するには、オプションになります。

ここに画像の説明を入力してください

ここに画像の説明を入力してください


3

ユースケース同じクラスの別の初期化子の初期化子を呼び出すことである場合は理にかなっています。

遊び場でこれをやってみてください

class Player {
    let name: String
    let level: Int

    init(name: String, level: Int) {
        self.name = name
        self.level = level
    }
    
    init(name: String) {
        self.init(name: name, level: 0) //<- Call the initializer above?

        //Sorry you can't do that. How about adding a convenience keyword?
    }
}

Player(name:"LoseALot")

便利キーワード付き

class Player {
    let name: String
    let level: Int

    init(name: String, level: Int) {
        self.name = name
        self.level = level
    }
    
    //Add the convenience keyword
    convenience init(name: String) {
        self.init(name: name, level: 0) //Yes! I am now allowed to call my fellow initializer!
    }
}

2

注:テキスト全体を読む

指定初期化子は、クラスの主要な初期化子です。指定された初期化子は、そのクラスによって導入されたすべてのプロパティを完全に初期化し、適切なスーパークラス初期化子を呼び出して、スーパークラスチェーンまで初期化プロセスを続行します。

コンビニエンスイニシャライザはセカンダリであり、クラスのイニシャライザをサポートします。コンビニエンス・イニシャライザーを定義して、コンビニエンス・イニシャライザーと同じクラスから、指定されたイニシャライザーのパラメーターの一部をデフォルトに設定して、指定されたイニシャライザーを呼び出すことができます。

クラスの指定された初期化子は、値型の単純な初期化子と同じ方法で記述されます。

init(parameters) {
statements
}

コンビニエンス初期化子は同じスタイルで記述されていますが、コンビニエンス修飾子がinitキーワードの前に配置され、スペースで区切られています。

convenience init(parameters) {
statements
}

実用的な例は次のとおりです。

class Food {
var name: String
init(name: String) {
    self.name = name
}
convenience init() {
    self.init(name: "[Unnamed]")
}
}
let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon”

Foodクラスのinit(name:String)初期化子は、新しいFoodインスタンスのすべての格納されたプロパティが完全に初期化されることを保証するため、指定された初期化子として提供されます。Foodクラスにはスーパークラスがないため、init(name:String)初期化子は初期化を完了するためにsuper.init()を呼び出す必要はありません。

「Foodクラスは、引数のない便利な初期化子init()も提供します。init()イニシャライザは、名前の値が[Unnamed]であるFoodクラスのinit(name:String)に委任することにより、新しい食品のデフォルトのプレースホルダー名を提供します。

let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]”

階層の2番目のクラスは、RecipeIngredientと呼ばれるFoodのサブクラスです。RecipeIngredientクラスは、料理レシピの材料をモデル化します。これは、(Foodから継承するnameプロパティに加えて)quantityというIntプロパティを導入し、RecipeIngredientインスタンスを作成するための2つの初期化子を定義します。

class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
    self.quantity = quantity
    super.init(name: name)
}
override convenience init(name: String) {
    self.init(name: name, quantity: 1)
}
}

RecipeIngredientクラスには、単一の指定された初期化子init(name:String、quantity:Int)があり、これを使用して、新しいRecipeIngredientインスタンスのすべてのプロパティを設定できます。この初期化子は、渡された数量引数を、RecipeIngredientによって導入された唯一の新しいプロパティであるquantityプロパティに割り当てることから始まります。その後、イニシャライザはFoodクラスのinit(name:String)イニシャライザに委任します。

ページ:536抜粋:Apple Inc.「Swiftプログラミング言語(Swift4)」。iBooks。https://itunes.apple.com/pk/book/the-swift-programming-language-swift-4-0-3/id881256329?mt=11


2

そのため、クラスのすべてのプロパティを指定する必要がない場合に便利です。したがって、たとえば、HPの値を100から始めてすべての冒険を作成したい場合は、次の便利なinitを使用して、名前を追加するだけです。これにより、コードが大幅に削減されます。

class Adventure { 

// Instance Properties

    var name: String
    var hp: Int
    let maxHealth: Int = 100

    // Optionals

    var specialMove: String?

    init(name: String, hp: Int) {

        self.name = name
        self.hp = hp
    }

    convenience init(name: String){
        self.init(name: name, hp: 100)
    }
}

1

すべての答えは良いように聞こえますが、簡単な例でそれを理解しましょう

class X{                                     
   var temp1
   init(a: Int){
        self.temp1 = a
       }

これで、クラスが別のクラスを継承できることがわかりました。

class Z: X{                                     
   var temp2
   init(a: Int, b: Int){
        self.temp2 = b
        super.init(a: a)
       }

この場合、クラスZのインスタンスを作成するときに、値「a」と「b」の両方を指定する必要があります。

let z = Z(a: 1, b: 2)

ただし、bの値のみを渡し、restで他の値をデフォルト値にする場合は、他の値をデフォルト値で初期化する必要があります。しかし、どのように待つのですか?そのUは、クラスでのみ前にそれを設定する必要があります。

//This is inside the class Z, so consider it inside class Z's declaration
convenience init(b: Int){
    self.init(a: 0, b: b)
}
convenience init(){
    self.init(a: 0, b: 0)
}

これで、変数に一部、すべて、またはまったく値を指定せずに、クラスZのインスタンスを作成できます。

let z1 = Z(b: 2)
let z2 = Z()
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.