Swift 4.2 日本語化計画 : Swift 4.2


初期化


初期化 は、使用するためのクラス、構造体、または列挙型のインスタンスを準備する手順です。この手順には、そのインスタンスに格納された各プロパティの初期値を設定し、新しいインスタンスを使用する準備ができる前に必要とされる全ての他の設定や初期化を含みます。


特定の型の新しいインスタンスを作成するために呼び出される特殊なメソッドのようなもので定義するのが イニシャライザ で、この初期化の手順を実装して下さい。Objective-C のイニシャライザとは異なり、Swift のイニシャライザは値を返しません。それらの主な役割は、それらが最初に使用される前に型の新しいインスタンスが正しく初期化されていることを保証することです。


クラス型のインスタンスは、デイニシャライザ を実装する事もでき、それはそのクラスのインスタンスが割り当て解除される直前に全てのカスタムクリーンアップを実行します。デイニシャライザの詳細については、デイニシャライザ を参照してください。



格納されたプロパティの初期値を設定


クラスと構造体は、そのクラスまたは構造体のインスタンスが作成される時までに、適切な初期値に、それらすべての格納されたプロパティを設定 しなければなりません。格納されたプロパティは、不確定な状態のままにすることはできません。


イニシャライザ内に格納されたプロパティの初期値を設定でき、またはプロパティの定義の一部としてデフォルトのプロパティ値を代入することによって、初期値を設定できます。これらのアクションは、以下のセクションで説明します。


注意: 格納されたプロパティにデフォルト値を代入し、またはイニシャライザ内でその初期値を設定すると、そのプロパティ値は、どのプロパティ監視者も呼び出すことなく、直接設定されます。


イニシャライザ


イニシャライザ は、特定の型の新しいインスタンスを作成するために呼美出されます。最も単純な形式では、イニシャライザは、init キーワードを使用して書かれた、パラメータなしのインスタンスメソッドのようになります:


  1. init() {
  2.         // perform some initialization here
  3. }


以下の例は、華氏 (Fahrenheit) のスケールで表した温度を格納するための Fahrenheit と言う新しい構造体を定義しています。Fahrenheit 構造体は、格納されたプロパティの、 Double 型である temperature を一つ持っています。


  1. struct Fahrenheit {
  2.         var temperature: Double
  3.         init() {
  4.                 temperature = 32.0
  5.         }
  6. }
  7. var f = Fahrenheit()
  8. print("The default temperature is \(f.temperature)° Fahrenheit")
  9. // prints "The default temperature is 32.0° Fahrenheit"


構造体は、パラメータなしの init の、一つのイニシャライザを定義しており、これは 32.0 の値 (華氏で表現された水の氷点) の格納された温度で初期化しています。



デフォルトのプロパティ値


上に示したように、イニシャライザ内から格納されたプロパティの初期値を設定できます。また、プロパティの宣言の一部として、デフォルトのプロパティ値 を指定できます。それが定義されていると、プロパティに初期値を代入することによって、デフォルトのプロパティ値を指定できます。



注意: プロパティが常に同じ初期値を取る場合、イニシャライザ内の値を設定するのではなく、デフォルト値を提供して下さい。最終結果は同じですが、デフォルト値はその宣言に、より密接にプロパティの初期化を結び付けます。それはイニシャライザをより短く、明確にし、そのデフォルト値からプロパティの型を推論することができます。デフォルトのイニシャライザと、イニシャライザの継承の利点を活用するためには、この章で後述するように、デフォルト値もそれを容易にします。


temperature プロパティが宣言された時点で、そのプロパティのデフォルト値を提供することで、より簡単な形で上記の例からの Fahrenheit 構造体を書くことができます。


  1. struct Fahrenheit {
  2.        var temperature = 32.0
  3. }


初期化のカスタマイズ


以下のセクションで説明するように、入力パラメータと optional のプロパティ型で、または初期化中に定数プロパティを代入することで、初期化プロセスをカスタマイズできます。



初期化パラメータ


型と、初期化プロセスをカスタマイズする値の名前を定義するために、イニシャライザの定義の一部として、初期化パラメータ を提供できます。初期化パラメータは、関数やメソッドのパラメータと同じ機能と構文を持っています。


以下の例は、摂氏 (Celsius) で表した温度を格納する、Celsius と言う構造体を定義しています。Celsius 構造体は、異なる温度スケールからの値を持つ構造体の新しいインスタンスを初期化する、init(fromFahrenheit:)init(fromKelvin:) と言う2つのカスタム・イニシャライザを実装しています。


  1. struct Celsius {
  2.        var temperatureInCelsius: Double
  3.        init(fromFahrenheit fahrenheit: Double) {
  4.                temperatureInCelsius = (fahrenheit - 32.0) / 1.8
  5.        }
  6.        init(fromKelvin kelvin: Double) {
  7.                temperatureInCelsius = kelvin - 273.15
  8.        }
  9. }
  10. let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
  11. // boilingPointOfWater.temperatureInCelsius is 100.0
  12. let freezingPointOfWater = Celsius(fromKelvin: 273.15)
  13. // freezingPointOfWater.temperatureInCelsius is 0.0


最初のイニシャライザには fromFahrenheit の引数ラベルと fahrenheit のパラメータ名を持つ一つの初期化パラメータがあります。二番目のイニシャライザには、fromKelvin の引数ラベルと kelvin のパラメータ名を持つ一つの初期化パラメータがあります。どちらのイニシャライザもそれら一つの引数を、対応する摂氏の値に変換し、temperatureInCelsius というプロパティにその値を格納します。



パラメータ名と引数ラベル


関数やメソッドのパラメータと同様に、初期化パラメータは、イニシャライザの本体内で使用するためのパラメータ名と、イニシャライザを呼び出す時に使用するための引数ラベルの両方を持つことができます。


しかし、イニシャライザはそれらの括弧の前に、関数やメソッドが持つような、識別する関数名を持っていません。そのため、イニシャライザのパラメータの名前と型は、どのイニシャライザを呼び出すべきか識別する際に特に重要な役割を果たしています。このことから、Swift は、引数名をあなた自身が提供していない場合、イニシャライザ内の すべての パラメータに自動引数ラベルを提供します。


以下の例では、red、green、そして blue と言う3つの定数プロパティを有する Color と言う構造体を定義しています。これらのプロパティは、色の赤、緑、および青の量を示す、0.01.0 の間の値を格納します。


Color は、その赤、緑、青の成分の Double 型の3つの適切な名前のパラメータを持つイニシャライザを提供しています。Color はまた、3つの色成分すべてに同じ値を提供するために使用される、一つの white のパラメータで、第二のイニシャライザも提供します。


  1. struct Color {
  2.         let red, green, blue: Double
  3.         init(red: Double, green: Double, blue: Double) {
  4.                 self.red = red
  5.                 self.green = green
  6.                 self.blue = blue
  7.         }
  8.         init(white: Double) {
  9.                 red = white
  10.                 green = white
  11.                 blue = white
  12.         }
  13. }


どちらのイニシャライザも、各イニシャライザパラメータの名前付きの値を提供することで、新しい Color のインスタンスを作成するために使用できます:


  1. let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
  2. let halfGray = Color(white: 0.5)


引数のラベルを使用することなく、これらのイニシャライザを呼び出すことができないことに注意してください。引数ラベルは、それらが定義されている場合常にイニシャライザで使用されなければならず、それらを省略すると、コンパイル時エラーになります:


  1. let veryGreen = Color(0.0, 1.0, 0.0)
  2. // this reports a compile-time error - argument labels are required


引数ラベルのないイニシャライザのパラメータ


イニシャライザパラメータに引数ラベルを使用したくない場合は、デフォルトの動作をオーバーライドするために、そのパラメータの明示的な引数ラベルを書く代わりに、アンダースコア (_) を書いてください。


ここで、上記の 初期化パラメータ から Celsius の例を拡張したものを挙げます。追加のイニシャライザを使用して、すでに摂氏スケールの Double 値から新しい Celsius インスタンスを作成します。


  1. struct Celsius {
  2.         var temperatureInCelsius: Double
  3.         init(fromFahrenheit fahrenheit: Double) {
  4.                 temperatureInCelsius = (fahrenheit - 32.0) / 1.8
  5.         }
  6.         init(fromKelvin kelvin: Double) {
  7.                 temperatureInCelsius = kelvin - 273.15
  8.         }
  9.         init(_ celsius: Double) {
  10.                 temperatureInCelsius = celsius
  11.         }
  12. }
  13. let bodyTemperature = Celsius(37.0)
  14. // bodyTemperature.temperatureInCelsius is 37.0


イニシャライザの Celsius(37.0) の呼び出しは、引数ラベルを必要とせずに、その意図で明らかです。そのため、それは無名の Double 値を提供することで呼び出すことができるように、init(_ celsius: Double) としてこのイニシャライザを書くことは適切です。


Optional のプロパティ型


カスタム型が、"値のない" 事を論理的に許される格納されたプロパティを持っている場合ーその値は初期化時に設定することは多分できないので、または、何らかの後の時点ーOptional 型のプロパティの宣言の時、"値のない" 事が許されているためです。Optional 型のプロパティは自動的に nil の値で初期化されるため、プロパティが初期化中に"まだ値がない" ことを故意に意図していることを示します。


以下の例では、response と言う Optional の String プロパティを持つ、SurveyQuestion というクラスを定義しています。


  1. class SurveyQuestion {
  2.         var text: String
  3.         var response: String?
  4.         init(text: String) {
  5.         self.text = text
  6.         }
  7.         func ask() {
  8.                 print(text)
  9.         }
  10. }
  11. let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
  12. cheeseQuestion.ask()
  13. // prints "Do you like cheese?"
  14. cheeseQuestion.response = "Yes, I do like cheese."


調査の質問への答えは、それが問われるまでは知ることができず、そのため、response プロパティは、String? の型つまり "Optional の String" で宣言されています。それには、SurveyQuestion の新しいインスタンスが初期化される時、"まだ文字列がない" を意味する nil のデフォルト値が自動的に代入されます。



初期化中に定数プロパティへの代入


初期化が終了する時までに確定した値を設定するかぎり、初期化中の任意の時点で定数プロパティに値を代入できます。



注意: クラス・インスタンスの場合、定数プロパティはそれを導入するクラスによってのみ初期化中に変更できます。これは、サブクラスによって変更できません。


SurveyQuestion の上記の例を修正して質問の text プロパティに、変数プロパティではなく定数プロパティを使うようにして、SurveyQuestion のインスタンスが一度作成されると question が変更されないことを示すようにします。text プロパティは、現在定数であっても、それはまだクラスのイニシャライザ内で設定することができます:


  1. class SurveyQuestion {
  2.         let text: String
  3.         var response: String?
  4.         init(text: String) {
  5.                 self.text = text
  6.         }
  7.         func ask() {
  8.                 print(text)
  9.         }
  10. }
  11. let beetsQuestion = SurveyQuestion(text: "How about beets?")
  12. beetsQuestion.ask()
  13. // prints "How about beets?"
  14. beetsQuestion.response = "I also like beets. (But not with cheese.)"


デフォルトのイニシャライザ


Swift は、そのすべてのプロパティにデフォルト値を提供し、それ自体少なくとも一つのイニシャライザを提供していない全ての構造体またはクラスに デフォルトのイニシャライザ を提供します。デフォルトのイニシャライザは、それらのデフォルト値に設定した、そのプロパティのすべてで新しいインスタンスを単に作成します。


以下の例では、買い物リスト内のアイテムの名前、量、及び購入状態をカプセル化した ShoppingListItem というクラスを定義しています。


  1. class ShoppingListItem {
  2.         var name: String?
  3.         var quantity = 1
  4.         var purchased = false
  5. }
  6. var item = ShoppingListItem()


ShoppingListItem クラスのすべてのプロパティにはデフォルト値があり、それはスーパークラスのない基底クラスであるため、ShoppingListItem は自動的に、それらのデフォルト値に設定されるそのプロパティのすべてに新しいインスタンスを作成する、デフォルトのイニシャライザの実装を獲得しています。(name プロパティは optional の String プロパティですので、この値はコードで書かれていませんが、nil のデフォルト値を自動的に受け取ります。) 上記の例では、ShoppingListItem() として書かれるイニシャライザの構文を持つクラスの新しいインスタンスを作成するために ShoppingListItem クラスのデフォルトイニシャライザを使用しており、そして item と言う変数に、この新しいインスタンスを代入します。



構造体型のためのメンバ化イニシャライザ


構造体型が独自のカスタムイニシャライザを全く定義しない場合、それらは、自動的に メンバ化イニシャライザ を受け取ります。デフォルトのイニシャライザとは異なり、構造体は、それがデフォルト値を持たない格納されたプロパティを持っている場合でも、メンバ化イニシャライザを受け取ります。


メンバ化イニシャライザは、新たな構造体インスタンスのメンバ・プロパティを初期化する近道の方法です。新しいインスタンスのプロパティ用の初期値は、名前でメンバ化イニシャライザに渡すことができます。


以下の例では、widthheight と言う二つのプロパティを持つ Size と言う構造体を定義しています。どちらのプロパティも 0.0 のデフォルト値を代入される Double 型であることが推測されます。


Size 構造体は、init(width:height:) のメンバ化イニシャライザを自動的に受け取り、新しい Size インスタンスを初期化するのにそれを使用できます:


  1. struct Size {
  2.         var width = 0.0, height = 0.0
  3. }
  4. let twoByTwo = Size(width: 2.0, height: 2.0)



値型のイニシャライザデリゲート


イニシャライザは、インスタンスの初期化の一部を実行するために他のイニシャライザを呼び出すことができます。イニシャライザデリゲート として知られるこのプロセスで、複数のイニシャライザ間でのコードの重複を避けることができます。


イニシャライザデリゲートがいかに働くかと、どのような形のデリゲートが許可されているかのルールについては、値型とクラス型で異なります。値型 (構造体と列挙型) は継承をサポートしておらず、それらだけが、自分自身を提供する別のイニシャライザにデリゲートすることができるので、それらのイニシャライザのデリゲートプロセスは、比較的簡単です。しかし、継承 で説明したようにクラスは、他のクラスから継承できます。これは、それらが継承するすべての格納されたプロパティが、初期化中に適切な値を代入されることを確実にするための追加的な責任がクラスにはあることを意味します。これらの責任については、以下の クラスの継承と初期化 で説明します。


値型の場合、独自のカスタム・イニシャライザを書くときに同じ値型から他のイニシャライザを参照するには self.init を使用して下さい。イニシャライザ内からだけ self.init を呼び出すことができます。


値型のカスタム・イニシャライザを定義する場合、その型用のデフォルト・イニシャライザ (または構造体である場合、メンバ化イニシャライザ) に、もはやアクセスできなくなることに注意してください。この制約は、より複雑なイニシャライザで提供される追加の必須設定では、誰かが誤って自動イニシャライザのいずれか一つを使用したことによる事態を防止できます。



注意: カスタムの値型は、デフォルトのイニシャライザとメンバ化イニシャライザで初期化可能にしたければ、また、独自のカスタムのイニシャライザで、値型のオリジナルの実装の一部としてではなく、拡張機能で、カスタムのイニシャライザを書いて下さい。詳細については、拡張機能 を参照してください。


以下の例では、幾何学の長方形を表すためにカスタムの Rect 構造体を定義しています。例では、SizePoint と言うサポートする2つの構造体を必要とし、両方共それらのプロパティのすべてに 0.0 のデフォルト値を提供します。


  1. struct Size {
  2.         var width = 0.0, height = 0.0
  3. }
  4. struct Point {
  5.         var x = 0.0, y = 0.0
  6. }


以下の3つの方法のうちどれか一つで Rect 構造体を初期化できます:originsize プロパティ値をそのデフォルトのゼロで初期化する事によって、または特定の原点とサイズを提供することで、または特定の中心点とサイズを提供する事によって。これらの初期化オプションは、Rect 構造体の定義の一部である3つのカスタムイニシャライザで表わされます。


  1. struct Rect {
  2.         var origin = Point()
  3.         var size = Size()
  4.         init() {}
  5.         init(origin: Point, size: Size) {
  6.                 self.origin = origin
  7.                 self.size = size
  8.         }
  9.         init(center: Point, size: Size) {
  10.                 let originX = center.x - (size.width / 2)
  11.                 let originY = center.y - (size.height / 2)
  12.                 self.init(origin: Point(x: originX, y: originY), size: size)
  13.         }
  14. }


最初の Rect イニシャライザ、init() は、機能的には、独自のカスタムイニシャライザを持っていない場合、構造体が受けただろう、デフォルトのイニシャライザと同じです。このイニシャライザには、中括弧 {} の空のペアで表される空の本体があります。このイニシャライザを呼び出すと、それらのプロパティの定義から、Point(x: 0.0, y: 0.0)Size(width: 0.0, height: 0.0) のデフォルト値で初期化された originsize プロパティの Rect インスタンスを返します。


  1. let basicRect = Rect()
  2. // basicRect's origin is (0.0, 0.0) and its size is (0.0, 0.0)


2つ目の Rect イニシャライザ、init(origin:size:)、は、独自のカスタムイニシャライザを持っていない場合、構造体が受けただろう、メンバ化イニシャライザと機能的には同じです。このイニシャライザは、適切な格納されたプロパティに、originsize 引数の値を単に代入します。


  1. let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
  2.                 size: Size(width: 5.0, height: 5.0))
  3. // originRect's origin is (2.0, 2.0) and its size is (5.0, 5.0)


3つ目の Rect イニシャライザ、init(center:size:) は、少し複雑です。これは、center の点と size の値に基づいて適切な原点を計算することによって始まります。それはその後 init(origin:size:) のイニシャライザを呼び出し(または デリゲート し)、適切なプロパティに新しい原点とサイズの値を格納します:


  1. let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
  2.         size: Size(width: 3.0, height: 3.0))
  3. // centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)


init(center:size:) イニシャライザは、それ自体適切なプロパティに originsize の新しい値を代入することができました。しかし、init(center:size:) イニシャライザにとっては、その機能を既に正確に提供している既存のイニシャライザを利用するほうが、便利(意図がより明確) です。



注意: init()init(origin:size:) イニシャライザを自分自身で定義せずに、この例を書く事く別の方法は、拡張機能 を参照してください。


クラスの継承と初期化


そのスーパークラスから継承したクラスの全てのプロパティを含むクラスの格納されたプロパティは全て、初期化中に初期値を代入され なければなりません。


Swift は、全ての格納されたプロパティが、初期値を確実に受け取るのを助ける、クラス型のイニシャライザを2種類定義しています。これらは、指定イニシャライザとコンビニエンスイニシャライザとして知られています。



指定イニシャライザとコンビニエンスイニシャライザ


指定イニシャライザ は、クラスの第一のイニシャライザです。指定イニシャライザはそのクラスによって導入されたすべてのプロパティを完全に初期化し、スーパークラスの連鎖の初期化プロセスを継続するために適切なスーパークラスのイニシャライザを呼び出します。


クラスは、非常に少数の指定イニシャライザしか持たない傾向があり、クラスにとって1つしかイニシャライザを持たないことが非常に一般的です。指定イニシャライザは、初期化が行われ、それを介して初期化プロセスがスーパークラスの連鎖を続ける事を通す "漏斗" 点です。


すべてのクラスは、少なくとも1つの指定イニシャライザを持っていなければなりません。いくつかの場合、この要件は、1つ以上の指定イニシャライザをスーパークラスから継承する事により満たされますが、この事は後に述べる 自動イニシャライザの継承 で説明します。


コンビニエンスイニシャライザ は、クラスのための二次的な、サポートするイニシャライザです。デフォルト値に設定する指定イニシャライザのパラメータの一部を持つコンビニエンスイニシャライザと同じクラスから指定イニシャライザを呼び出すためにコンビニエンスイニシャライザを定義できます。また、特定の使用ケースや入力の値型のため、そのクラスのインスタンスを作成するためにコンビニエンスイニシャライザも定義できます。


クラスがコンビニエンスイニシャライザを必要としない場合には、それらを提供する必要はありません。一般的な初期化パターンへの近道が時間を節約するか、意図を明確にしてクラスの初期化を行う時にはいつでも、コンビニエンスイニシャライザを作成して下さい。



指定とコンビニエンスイニシャライザの構文


クラスの指定イニシャライザは値型の単純なイニシャライザと同じように書かれます:


init( ) {

       

}



コンビニエンスイニシャライザは、同じスタイルで書かれますが、空白で区切って init キーワードの前に convenience 修飾子を書きます。


convenience init( ) {

       

}



クラス型のイニシャライザデリゲート


指定とコンビニエンスイニシャライザの間の関係を簡単にするため、Swift は、以下の3つのルールをイニシャライザ間のデリゲート呼び出しに適用します。


規則その1

指定イニシャライザは、そのすぐ上のスーパークラスから指定イニシャライザを呼び出さなければなりません。


規則その2

コンビニエンスイニシャライザは、同じ クラスから別のイニシャライザを呼び出さなければなりません。


規則その3

コンビニエンスイニシャライザは、最終的には指定イニシャライザを呼び出さなければなりません。


これを覚える簡単な方法は次のとおりです。


これらの規則を、以下の図に示します:



initializerDelegation01_2x


ここで、スーパークラスは、一つの指定イニシャライザと2つのコンビニエンスイニシャライザを持っています。一つのコンビニエンスイニシャライザは別のコンビニエンスイニシャライザを呼び出し、それは順番に、一つの指定イニシャライザを呼び出します。これは、上記の規則その2およびその3を満足します。スーパークラスは、それ自体はさらなるスーパークラスを持っておらず、したがって規則その1は適用されません。


この図のサブクラスは、2つの指定イニシャライザと1つのコンビニエンスイニシャライザを持っています。コンビニエンスイニシャライザは、同じクラスからは別のイニシャライザしか呼び出せないので、2つの指定イニシャライザのうち一つを呼び出さなければなりません。これは、上記の規則その2およびその3を満足します。両方の指定イニシャライザとも、上記の規則その1を満たすために、スーパークラスから一つの指定イニシャライザを呼び出さなければなりません。


注意: これらの規則は、クラスのユーザーが、各クラスのインスタンスを 作成する 方法には影響しません。上図の全てのイニシャライザは、それらが属するクラスの、完全に初期化されたインスタンスを作成するために使用できます。規則は、あなたがクラスのイニシャライザの実装をどう書くかのみに影響します。


下の図は、4つのクラスの、より複雑なクラス階層を示しています。これは、連鎖内のクラス間の相互関係を簡素化する、クラスの初期化の "漏斗" 点として、この階層内でどのように指定イニシャライザが振る舞うかを示しています。


initializerDelegation02_2x



二相の初期化


Swift におけるクラスの初期化は2相のプロセスを経ます。第一相では、格納された各プロパティは、それを導入したクラスによって初期値が代入されます。すべての格納されたプロパティの初期状態が一度決定されると、第二相が開始し、各クラスは、新しいインスタンスが使用可能な状態とみなされるずっと前に、その格納されたプロパティをカスタム化する機会が与えられます。


二相の初期化プロセスの使用は、初期化を安全にし、クラス階層内の各クラスに完全な柔軟性をも与えます。二相の初期化は、それらが初期化される前に、アクセスされる事をプロパティ値から防ぎ、かつ予想外に、別のイニシャライザによって異なる値に設定される事からプロパティ値を防ぎます。



注意: Swift の二相の初期化プロセスは、Objective-C での初期化と似ています。主な違いは、第一段階の間に、Objective-C では、ゼロまたは NULL 値 (0nil のような) をすべてのプロパティに代入することです。Swift の初期化の流れは、カスタムの初期値を設定し、0nil が有効なデフォルト値ではない型に対応できることで、より柔軟性があります。


二相の初期化がエラーなしで完了したことを Swift のコンパイラは確認するために、役立つ安全チェックを4つ実行します。


安全確認その1

指定イニシャライザは、そのクラスによって導入されたプロパティがすべて、スーパークラスのイニシャライザにまでデリゲートする前に初期化されることを確認しなければなりません。


上記で述べたように、オブジェクトのメモリは、その格納されたプロパティのすべての初期状態が知られた時にのみ、完全に初期化されたとみなされます。この規則を満足するために、指定イニシャライザは、連鎖を登って行く前に、すべての独自のプロパティが初期化されていることを確認しなければなりません。


安全確認その2

指定イニシャライザは、継承されたプロパティに値を代入する前に、スーパークラスのイニシャライザまでデリゲートしなければなりません。そうでないと、指定イニシャライザが代入した新しい値は、独自の初期化の一部として、スーパークラスによって上書きされます。


安全確認その3

コンビニエンスイニシャライザは、全ての プロパティに値を代入する前に、別のイニシャライザにデリゲートしなければなりません (同じクラスで定義されたプロパティを含む)。そうでないと、コンビニエンスイニシャライザが代入した新しい値は、独自のクラスの指定イニシャライザで上書きされます。


安全確認その4

イニシャライザは、全てのインスタンスメソッドを呼び出す事ができるとは限らず、全てのインスタンス·プロパティの値を読む事、または初期化の第一相が完了するまでは値として self を参照する事もできません。


クラスのインスタンスは、第一相が終了するまでは、完全には有効でありません。プロパティは、アクセスすることだけができ、クラスインスタンスが第一相の終了時に有効であることさえ分かれば、メソッドは、呼び出すことだけができます。


ここで、二相の初期化が上記の4つの安全確認に基づいて、果たす役割は以下のとおりです。


第一相

第二相

ここで、第一相が、仮想的なサブクラスとスーパークラスの初期化呼び出しでどのように見えるかを示します:



twoPhaseInitialization01_2x


この例では、初期化は、サブクラスのコンビニエンスイニシャライザへの呼び出しで始まります。このコンビニエンスイニシャライザは、まだどのプロパティも変更できません。同じクラスからの指定イニシャライザを横切ってデリゲートして行きます。


指定イニシャライザは、安全確認その1に従って、サブクラスのすべてのプロパティが値を持っていることを確認します。そして、連鎖を登って初期化を継続して、そのスーパークラスの指定イニシャライザを呼び出します。


スーパークラスの指定イニシャライザは、スーパークラスのすべてのプロパティが値を持っていることを確認します。そこに初期化すべき更なるスーパークラスはないので、したがって、それ以上のデリゲートは必要ありません。


スーパークラスのすべてのプロパティが初期値を持つやいなや、そのメモリは完全に初期化しているとみなされ、第一相が完了します。


ここで第二相が同じ初期化呼び出しでどのように見えるか示します:



twoPhaseInitialization02_2x


スーパークラスの指定イニシャライザには (それは必要ではありませんが) さらにインスタンスをカスタム化する機会があります。


スーパークラスの指定イニシャライザが終了すると、サブクラスの指定イニシャライザは、(再び、それは必要ではありませんが) 追加のカスタム化を行うことができます。


最後に、サブクラスの指定イニシャライザが終了すると、元々呼ばれていたコンビニエンスイニシャライザは追加のカスタム化を行うことができます。



イニシャライザ継承とオーバーライド


Objective-C のサブクラスとは異なり、Swift のサブクラスは、デフォルトでは、そのスーパークラスのイニシャライザを継承しません。Swift のアプローチは、スーパークラスからの簡単なイニシャライザが、より専門的なサブクラスによって継承され、完全にまたは正しく初期化されないサブクラスの新しいインスタンスを作成するために使用されている状況を防ぐことができます。


注意: スーパークラスのイニシャライザは、ある特定の状況では継承 されます が、それは、安全かつ適切な場合にのみ、そのようにされます。詳細については、下記の 自動イニシャライザの継承 を参照してください。

カスタムサブクラスがそのスーパークラスと1つ以上の同じイニシャライザを提示したい場合には、サブクラス内のそれらのイニシャライザのカスタムの実装を提供できます。


スーパークラスの 指定 イニシャライザに一致するサブクラスイニシャライザを書くときには、その指定イニシャライザのオーバーライドを効果的に提供できます。そのため、サブクラスのイニシャライザの定義の前に override 修飾子を書かなければなりません。これは、デフォルトのイニシャライザ で説明したように、自動的に提供されているデフォルトのイニシャライザをオーバーライドしている場合にも当てはまります。


オーバーライドされたプロパティ、メソッド、またはサブスクリプトの場合と同様に、override 修飾子の存在は、スーパークラスがオーバーライドされるべき一致する指定イニシャライザがあることを確認するように Swift を促して、意図したとおりに、オーバーライドするイニシャライザのためのパラメータが指定されていることを検証します。


注意: イニシャライザのサブクラスの実装がコンビニエンスイニシャライザであっても、スーパークラスの指定イニシャライザをオーバーライドするときは、必ず override 修飾子を書いて下さい。


逆に、スーパークラスの コンビニエンス イニシャライザに一致するサブクラスイニシャライザを書く場合、そのスーパークラスコンビニエンスイニシャライザは クラス型のイニシャライザデリゲート で上述した規則に従って、サブクラスによって直接呼び出されることは決してありません。そのため、サブクラスは (厳密に言えば) スーパークラスイニシャライザのオーバーライドを提供していません。その結果、スーパークラスのコンビニエンスイニシャライザに一致する実装を提供するときには override 修飾子を書かないで下さい。


以下の例では、Vehicle と言う基本クラスを定義しています。この基本クラスは numberOfWheels と言う、 0Int デフォルト値を持つ、格納されたプロパティを宣言しています。numberOfWheels プロパティは、車両の特性の String の説明を作成するために description と言う計算されたプロパティで使用されます。


  1. class Vehicle {
  2.        var numberOfWheels = 0
  3.        var description: String {
  4.                return "\(numberOfWheels) wheel(s)"
  5.        }
  6. }


Vehicle クラスは、唯一の格納されたプロパティ用のデフォルト値を提供し、それ自身は全くカスタムイニシャライザを提供していません。その結果、デフォルトのイニシャライザ で説明したように、デフォルトのイニシャライザを自動的に受け取ります。デフォルトのイニシャライザ (使用可能な時) は、常にクラスの指定イニシャライザで、0numberOfWheels で新しい Vehicle インスタンスを作成するために使用できます:


  1. let vehicle = Vehicle()
  2. print("Vehicle: \(vehicle.description)")
  3. // Vehicle: 0 wheel(s)


次の例では、Bicycle と言う Vehicle のサブクラスを定義しています:


  1. class Bicycle: Vehicle {
  2.         override init() {
  3.                 super.init()
  4.                 numberOfWheels = 2
  5.         }
  6. }


Bicycle サブクラスはカスタム指定イニシャライザ、init() を定義しています。この指定イニシャライザは、Bicycle のスーパークラスからの指定イニシャライザと一致するので、このイニシャライザの Bicycle バージョンは override 修飾子でマークされています。


Bicycleinit() イニシャライザは super.init() を呼び出すことで始まり、Bicycle クラスのスーパークラスである、 Vehicle のデフォルトのイニシャライザを呼び出します。これは Bicycle がプロパティを変更する機会を得る前に numberOfWheels の継承したプロパティが、Vehicle によって初期化されることを保証します。 super.init() を呼び出した後、numberOfWheels の元の値は新しい値である 2 に置き換えられます。


Bicycle のインスタンスを作成すると、その継承された description の計算されたプロパティを呼び出して、その numberOfWheels プロパティがどのように更新されたかを確認できます。


  1. let bicycle = Bicycle()
  2. print("Bicycle: \(bicycle.description)")
  3. // Bicycle: 2 wheel(s)


サブクラスイニシャライザが初期化プロセスの第二相でカスタマイズを行わず、スーパークラスにゼロ個の引数の指定イニシャライザがある場合は、すべてのサブクラスの保管されたプロパティに値を代入した後の super.init() の呼び出しを省略できます。


この例では、Hoverboard と呼ばれる Vehicle の別のサブクラスを定義しています。そのイニシャライザでは、Hoverboard クラスはその color プロパティだけを設定します。super.init() を明示的に呼び出すのではなく、このイニシャライザはプロセスを完了するためにそのスーパークラスイニシャライザへの暗黙の呼び出しに依存します。


  1. class Hoverboard: Vehicle {
  2.         var color: String
  3.         init(color: String) {
  4.                 self.color = color
  5.                 // super.init() implicitly called here
  6.         }
  7.         override var description: String {
  8.                 return "\(super.description) in a beautiful \(color)"
  9.         }
  10. }


Hoverboard のインスタンスは、Vehicle イニシャライザによって提供されるデフォルトのホイール数を使用します。


  1. let hoverboard = Hoverboard(color: "silver")
  2. print("Hoverboard: \(hoverboard.description)")
  3. // Hoverboard: 0 wheel(s) in a beautiful silver


注意: サブクラスは、初期化中に、継承した変数のプロパティを変更することができますが、継承した定数のプロパティを変更することはできません。


自動イニシャライザの継承


前述したように、サブクラスはデフォルトでは、そのスーパークラスのイニシャライザを継承しません。しかし、特定の条件が満たされた場合は、スーパークラスのイニシャライザが自動的に継承 されます。実際には、これは多くの一般的なシナリオでイニシャライザオーバーライドを書く必要がない事を意味し、そうすることが安全であるときはいつでも、最小限の努力でスーパークラスのイニシャライザを継承できることを意味します。


サブクラスで導入したいくつかの新しいプロパティのデフォルト値を提供すると仮定すると、以下の2つの規則が適用されます。


規則その1

サブクラスが、全く指定イニシャライザを定義しない場合には、そのスーパークラスの指定イニシャライザのすべてを自動的に継承します。


規則その2

サブクラスは、そのスーパークラスの指定イニシャライザの 全て の実装を提供しますが、規則その1のようにそれらを継承することによって、またはその定義の一部として、カスタムの実装を提供することのどちらかで行い、それはスーパークラスのコンビニエンスイニシャライザのすべてを自動的に継承します。


これらの規則は、サブクラスがさらにコンビニエンスイニシャライザを追加した場合でも適用されます。



注意: サブクラスは、規則その2を満たす一環として、サブクラスのコンビニエンスイニシャライザとして、スーパークラスの指定イニシャライザを実装できます。



指定とコンビニエンスイニシャライザの実際


以下の例は、実際の指定イニシャライザ、コンビニエンスイニシャライザ、及び自動イニシャライザの継承を示しています。この例は、Food、RecipeIngredient、 および ShoppingListItem と言う3つのクラスの階層を定義し、それらのイニシャライザがどのように相互作用するかを示しています。


階層内の基本クラスは、食品の名前をカプセル化する単純なクラスである、Foodと言う名前です。Food クラスは、name と言う一つの String プロパティを導入し、Food インスタンスを作成するための2つのイニシャライザを提供します。


  1. class Food {
  2.         var name: String
  3.         init(name: String) {
  4.                 self.name = name
  5.         }
  6.         convenience init() {
  7.                 self.init(name: "[Unnamed]")
  8.         }
  9. }


以下の図は、Food クラスのイニシャライザ連鎖を示しています。



initializersExample01_2x


クラスは、デフォルトのメンバ化イニシャライザを持っていないので、Food クラスは、name という一つの引数を取る指定イニシャライザを提供しています。このイニシャライザは、特定の名前を持つ新しい Food インスタンスを作成するために使用できます:


  1. let namedMeat = Food(name: "Bacon")
  2. // namedMeat's name is "Bacon"


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


Food クラスはまた、引数なしの コンビニエンス イニシャライザ、init() も、提供しています。init() イニシャライザが Food クラスの init(name: String) に渡ってデリゲートすることで新たな food のデフォルトのプレースホルダ名を [Unnamed]name 値で提供します:


  1. let mysteryMeat = Food()
  2. // mysteryMeat's name is "[Unnamed]"


階層内の第二のクラスは RecipeIngredient と言う Food のサブクラスです。RecipeIngredient クラスは、料理レシピの原料をモデルにしています。これは、RecipeIngredient インスタンスを作成するために2つのイニシャライザを定義して (Food から継承する name プロパティに加えて) quantity と言う Int プロパティを導入しています。


  1. class RecipeIngredient: Food {
  2.         var quantity: Int
  3.         init(name: String, quantity: Int) {
  4.                 self.quantity = quantity
  5.                 super.init(name: name)
  6.         }
  7.         override convenience init(name: String) {
  8.                 self.init(name: name, quantity: 1)
  9.         }
  10. }


以下の図は、RecipeIngredient クラスのイニシャライザの連鎖を示しています。


initializersExample02_2x


RecipeIngredient クラスは、一つの指定イニシャライザを持っており、それは新しい RecipeIngredient インスタンスのすべてのプロパティを設定するために使用できる、init(name: String,quantity: Int) です。このイニシャライザは 渡された quantity 引数を、RecipeIngredient によって導入された唯一の新しいプロパティである、 quantity プロパティに代入することによって開始します。そうするとイニシャライザは、Foodクラスの init(name: String) までのイニシャライザをデリゲートします。このプロセスは、上記の 二相の初期化 の安全確認その1を満たします。


RecipeIngredient もコンビニエンスイニシャライザ、init(name: String) を定義し、名前だけで RecipeIngredient インスタンスを作成するために使用されます。このコンビニエンスイニシャライザは、明示的な量 (quantity) なしで作られている全ての RecipeIngredient インスタンスに 1 の量を想定しています。このコンビニエンスイニシャライザの定義は RecipeIngredient インスタンスをより速く、より便利に作成できるようにし、そしていくつかのちょっとした量の RecipeIngredient インスタンスを作成するときにコードの重複を避けることができます。このコンビニエンスイニシャライザは 1quantity の値を渡して、クラスの指定イニシャライザを横切って渡り単にデリゲートします。


init(name: String) コンビニエンスイニシャライザは、Food からの 指定 イニシャライザの init(name: String) と同じパラメータを取る RecipeIngredient が提供します。このコンビニエンスイニシャライザは、そのスーパークラスからの指定イニシャライザをオーバーライドしますので (イニシャライザ継承とオーバーライド で説明したように)、それは override 修飾子でマークされなければなりません。


RecipeIngredient は、init(name: String) イニシャライザをコンビニエンスイニシャライザとして提供していますが、RecipeIngredient は、それにもかかわらず、そのスーパークラスの指定イニシャライザのすべての実装を提供しています。したがって、RecipeIngredient はそのスーパークラスのコンビニエンスイニシャライザのすべてを自動的に継承します。


この例では、RecipeIngredient のスーパークラスは、Food で、それは init() と言う一つのコンビニエンスイニシャライザを持っています。このイニシャライザは、したがって RecipeIngredient によって継承されます。 init() 関数の継承バージョンは Food バージョン と完全に同じく機能しますが、Food バージョンと言うよりは init(name: String)RecipeIngredient バージョンへデリゲートする事を除きます。


これらの3つのイニシャライザは全部、新しい RecipeIngredient インスタンスを作成するために使用できます:


  1. let oneMysteryItem = RecipeIngredient()
  2. let oneBacon = RecipeIngredient(name: "Bacon")
  3. let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)


階層内の3番目で最後のクラスは ShoppingListItem と言う RecipeIngredient のサブクラスです。ShoppingListItem クラスは、買い物リストに表われるレシピの原料をモデルにしています。


買い物リスト内のすべての項目は、"未購入" として始まります。この事実を表現して、ShoppingListItem は、false のデフォルト値を持つ purchased と言うブール型プロパティを導入します。ShoppingListItem はまた計算された description プロパティを追加し、これは ShoppingListItem インスタンスのテキスト記述を提供します:


  1. class ShoppingListItem: RecipeIngredient {
  2.         var purchased = false
  3.         var description: String {
  4.                 var output = "\(quantity) x \(name)"
  5.                 output += purchased ? " ✔" : " ✘"
  6.                 return output
  7.         }
  8. }


注意: ShoppinListItem は、買い物リストの項目は (ここでのモデルとしては) 常に未購入から始めるので、purchased の初期値を提供するイニシャライザは定義していません。


それが導入した全てのプロパティのデフォルト値を提供し、どんなイニシャライザもそれ自身定義していないので、ShoppingListItem はそのスーパークラスからの指定とコンビニエンスイニシャライザの すべて を自動的に継承します。


以下の図は、3つのクラスすべての全体的なイニシャライザ連鎖を示しています。


initializersExample03_2x


新しい ShoppingListItem インスタンスを作成するために、継承されたイニシャライザの3つすべてを使用できます。


  1. var breakfastList = [
  2.         ShoppingListItem(),
  3.         ShoppingListItem(name: "Bacon"),
  4.         ShoppingListItem(name: "Eggs", quantity: 6),
  5. ]
  6. breakfastList[0].name = "Orange juice"
  7. breakfastList[0].purchased = true
  8. for item in breakfastList {
  9.         print(item.description)
  10. }
  11. // 1 x Orange juice ✔
  12. // 1 x Bacon ✘
  13. // 6 x Eggs ✘


ここで、breakfastList と言う新しい配列が3つの新しい ShoppingListItem インスタンスを含む配列リテラルから作成されます。配列の型は、[ShoppingListItem] であることが推測されます。配列が作成された後、配列の先頭の ShoppingListItem の名前は "[Unnamed]" から "Orange juice" に変更され、それが購入されたものとしてマークされます。配列内の各項目の説明を印刷すると、予想されるようにデフォルト状態が設定されていることを示します。



失敗可能イニシャライザ


初期化が失敗する可能性があるクラス、構造体、または列挙型を定義すると便利な場合があります。この失敗は、無効な初期化パラメータ値によって引き起こされる場合があり、必要な外部リソースがない場合、または、初期化が成功する事を妨げるいくつかの他の条件によって起こります。


初期化条件が失敗する場合に対処するために、クラス、構造体、または列挙型の定義の一部として、1つ以上の失敗可能(failable)イニシャライザを定義できます。init キーワードの後に疑問符を配置して失敗可能イニシャライザを書いて下さい(init?)。



注意: failable と同じパラメータ型と名前を持つ nonfailable イニシャライザを定義することはできません。


失敗可能(failable)イニシャライザは、初期化する型の optional 値を作成します。初期化の失敗が引き起こされる点を示すために、失敗可能イニシャライザ内に return nil を書いて下さい。



注意: 厳密に言えば、イニシャライザは値を返しません。むしろ、その役割は、初期化が終わった時点で self が完全に正しく初期化された事を保証することです。初期化の失敗を引き起こすために return nil を書きますが、初期化の成功を示すために、return キーワードを使用しないでください。


たとえば、数値型の変換のために 失費可能 (failable) イニシャライザが実装されます。数値型間の変換を確実に維持するには、init(exactly:) イニシャライザを使用して下さい。型変換が値を維持できない場合、イニシャライザは失敗します。


  1. let wholeNumber: Double = 12345.0
  2. let pi = 3.14159
  3. if let valueMaintained = Int(exactly: wholeNumber) {
  4.         print("\(wholeNumber) conversion to Int maintains value of \(valueMaintained)")
  5. }
  6. // Prints "12345.0 conversion to Int maintains value of 12345"
  7. let valueChanged = Int(exactly: pi)
  8. // valueChanged is of type Int?, not Int
  9. if valueChanged == nil {
  10.         print("\(pi) conversion to Int does not maintain value")
  11. }
  12. // Prints "3.14159 conversion to Int does not maintain value"


以下の例では、species と言う定数の String プロパティと、Animal と言う構造体を定義しています。Animal の構造体はまた、species と言う一つのパラメータで失敗可能イニシャライザも定義しています。このイニシャライザは、イニシャライザに渡された species の値が空の文字列であるかどうかチェックします。空の文字列が見つかった場合、初期化エラーが引き起こされます。そうでなければ、species プロパティ値が設定され、初期化は成功します。


  1. struct Animal {
  2.         let species: String
  3.         init?(species: String) {
  4.                 if species.isEmpty { return nil }
  5.                 self.species = species
  6.         }
  7. }


新しい Animal インスタンスを初期化し、初期化が成功したかどうかを確認しようとするにはこの失敗可能イニシャライザを使用できます。


  1. let someCreature = Animal(species: "Giraffe")
  2. // someCreature is of type Animal?, not Animal
  3. if let giraffe = someCreature {
  4.         print("An animal was initialized with a species of \(giraffe.species)")
  5. }
  6. // prints "An animal was initialized with a species of Giraffe"


失敗可能イニシャライザの species パラメータに空の文字列値を渡すと、イニシャライザは、初期化失敗を引き起こします:


  1. let anonymousCreature = Animal(species: "")
  2. // anonymousCreature is of type Animal?, not Animal
  3. if anonymousCreature == nil {
  4.         print("The anonymous creature could not be initialized")
  5. }
  6. // prints "The anonymous creature could not be initialized"


注意: 空の文字列値 (例えば、"Giraffe" ではなく"") のチェックは、optionalString 値が存在しないことを示すために nil をチェックするのと同じではありません。上記の例では、空の文字列 ("") は、有効な、optional でない String です。しかし、animal がその species プロパティの値として空の文字列を持つのは適切ではありません。この制限をモデル化するために、空の文字列が見つかった場合は、失敗可能イニシャライザは、初期化の失敗を引き起こします。



列挙型のための失敗可能イニシャライザ


1つ以上のパラメータに基づいて、適切な列挙型の case を選択する失敗可能イニシャライザを使用できます。提供されたパラメータが適切な列挙型の case に一致しない場合は、イニシャライザは失敗する可能性があります。


以下の例では、3つの可能な状態 (kelvin 絶対温度、celsius 摂氏、そして fahrenheit 華氏) で、TemperatureUnit と言う列挙型を定義しています。失敗可能イニシャライザは、温度記号を表す Character 値の適切な列挙型の case を見つけるために使用されています。


  1. enum TemperatureUnit {
  2.         case kelvin, celsius, fahrenheit
  3.         init?(symbol: Character) {
  4.                 switch symbol {
  5.                 case "K":
  6.                         self = .kelvin
  7.                 case "C":
  8.                         self = .celsius
  9.                 case "F":
  10.                         self = .fahrenheit
  11.                 default:
  12.                         return nil
  13.                 }
  14.         }
  15. }


3つの可能な状態の適切な列挙型の case を選択して、パラメータがこれらのいずれの状態とも一致しない場合、初期化が失敗を引き起こすと、この失敗可能イニシャライザを使用できます。


  1. let fahrenheitUnit = TemperatureUnit(symbol: "F")
  2. if fahrenheitUnit != nil {
  3.         print("This is a defined temperature unit, so initialization succeeded.")
  4. }
  5. // prints "This is a defined temperature unit, so initialization succeeded."
  6. let unknownUnit = TemperatureUnit(symbol: "X")
  7. if unknownUnit == nil {
  8.         print("This is not a defined temperature unit, so initialization failed.")
  9. }
  10. // prints "This is not a defined temperature unit, so initialization failed."


生の値を持つ列挙型のための失敗可能イニシャライザ


生の値を持つ列挙型は、自動的に init?(rawValue:) の失敗可能イニシャライザを受け取り、それは適切な生の値型の rawValue というパラメータを取り、一つ見つかった場合、一致する列挙型の case を選択し、または一致する値がない場合、初期化の失敗を引き起こします。


Character 型の生の値を使用するようにして、init?(rawValue:) イニシャライザを利用するには、上記の TemperatureUnit の例を書き換えて下さい:


  1. enum TemperatureUnit: Character {
  2.         case kelvin = "K", celsius = "C", fahrenheit = "F"
  3. }
  4. let fahrenheitUnit = TemperatureUnit(rawValue: "F")
  5. if fahrenheitUnit != nil {
  6.         print("This is a defined temperature unit, so initialization succeeded.")
  7. }
  8. // prints "This is a defined temperature unit, so initialization succeeded."
  9. let unknownUnit = TemperatureUnit(rawValue: "X")
  10. if unknownUnit == nil {
  11.         print("This is not a defined temperature unit, so initialization failed.")
  12. }
  13. // prints "This is not a defined temperature unit, so initialization failed."



初期化失敗の伝播


クラス、構造体、または列挙型の失敗可能イニシャライザは、同じクラス、構造体、または列挙型から別の失敗可能イニシャライザへ横切ってデリゲートできます。同様に、サブクラスの失敗可能イニシャライザは、スーパークラスの失敗可能イニシャライザまで登ってデリゲートできます。


どちらの場合でも、初期化に失敗する原因となる別のイニシャライザにデリゲートする場合、全体の初期化処理はすぐに失敗し、それ以上の初期化コードは実行されません。


注意: failable (失敗可能) イニシャライザも、nonfailable イニシャライザにデリゲートできます。そうでなければ失敗しない既存の初期化プロセスへの潜在的な失敗の状態を追加する必要がある場合は、この方法を使用して下さい。


以下の例は、CartItem と言う Product のサブクラスを定義しています。CartItem クラスは、オンラインのショッピングカート内のアイテムをモデルにしています。CartItem は、quantity と言う格納された定数プロパティを導入し、このプロパティは少なくとも 1 の値を常に持っていることを保証します。


  1. class Product {
  2.         let name: String
  3.         init?(name: String) {
  4.                 if name.isEmpty { return nil }
  5.                 self.name = name
  6.         }
  7. }
  8. class CartItem: Product {
  9.         let quantity: Int
  10.         init?(name: String, quantity: Int) {
  11.                 if quantity < 1 { return nil }
  12.                 self.quantity = quantity
  13.                 super.init(name: name)
  14.         }
  15. }


CartItem 用の failable(失敗可能) イニシャライザは、それが 1 以上の quantity の値を受信したことを検証することにより開始します。quantity が無効の場合、全体の初期化プロセスはすぐに失敗し、さらなる初期化コードは実行されません。同様に、Product 用の failable(失敗可能) イニシャライザは、name 値をチェックし、name が空の文字列の場合、初期化プロセスはすぐに失敗します。


あなたが空でない name と 1 以上の quantity で CartItem インスタンスを作成した場合、初期化は成功します。


  1. if let twoSocks = CartItem(name: "sock", quantity: 2) {
  2.         print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
  3. }
  4. // prints "Item: sock, quantity: 2"


quantity0CartItem インスタンスを作成しようとすると、CartItem イニシャライザは初期化に失敗します。


  1. if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
  2.         print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
  3. } else {
  4.         print("Unable to initialize zero shirts")
  5. }
  6. // prints "Unable to initialize zero shirts"


同様に、空の name 値で CartItem インスタンスを作成しようとした場合も、スーパークラスの Product イニシャライザは初期化に失敗します。


  1. if let oneUnnamed = CartItem(name: "", quantity: 1) {
  2.         print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
  3. } else {
  4.         print("Unable to initialize one unnamed product")
  5. }
  6. // prints "Unable to initialize one unnamed product"



失敗可能イニシャライザのオーバーライド


他のすべてのイニシャライザのように、サブクラス内でスーパークラスの失敗可能イニシャライザをオーバーライドすることができます。あるいは、サブクラスの 失敗できない(nonfailable) イニシャライザでスーパークラスの失敗可能イニシャライザをオーバーライドすることもできます。これで、スーパークラスの初期化ザが失敗することを許可されていても、初期化が失敗できないようにサブクラスを定義できるようになります。


失敗できないサブクラスのイニシャライザで失敗可能なスーパークラスのイニシャライザをオーバーライドする場合、スーパークラスのイニシャライザまで登って行きデリゲートする唯一の方法は、スーパークラスの失敗可能イニシャライザの結果を強制的に開封することです。



注意: 失敗できないイニシャライザで失敗可能イニシャライザをオーバーライドできますが、その逆はできません。


以下の例では、Document と言うクラスを定義しています。このクラスは、空でない文字列値か nil のいずれかの name プロパティで初期化することができ、空の文字列にはならない document をモデルにしています。


  1. class Document {
  2.         var name: String?
  3.         // this initializer creates a document with a nil name value
  4.         init() {}
  5.         // this initializer creates a document with a non-empty name value
  6.         init?(name: String) {
  7.                 if name.isEmpty { return nil }
  8.                 self.name = name
  9.         }
  10. }


次の例では、AutomaticallyNamedDocument と言う Document のサブクラスを定義しています。 AutomaticallyNamedDocument サブクラスは、Document によって導入された指定イニシャライザを両方ともオーバーライドします。これらのオーバーライドは AutomaticallyNamedDocument インスタンスが "[Untitled]" の初期の name 値を持っていることを、インスタンスが名前なしで初期化されるか、または空の文字列が init(name:) イニシャライザに渡されるかどうかを確認します:


  1. class AutomaticallyNamedDocument: Document {
  2.         override init() {
  3.                 super.init()
  4.                 self.name = "[Untitled]"
  5.         }
  6.         override init(name: String) {
  7.                 super.init()
  8.                 if name.isEmpty {
  9.                         self.name = "[Untitled]"
  10.                 } else {
  11.                         self.name = name
  12.                 }
  13.         }
  14. }


AutomaticallyNamedDocument は nonfailable の (失敗できない) init(name:) イニシャライザでスーパークラスの失敗可能 init?(name:) イニシャライザをオーバーライドしています。AutomaticallyNamedDocument はそのスーパークラスとは異なる方法で、空の文字列の場合に対応するので、そのイニシャライザは失敗する必要がなく、代わりにイニシャライザの nonfailable (失敗できない) バージョンを提供しています。


サブクラスの nonfailable (失敗できない)イニシャライザの実装の一部として、スーパークラスから失敗可能イニシャライザを呼び出すためにイニシャライザで強制的開封を使用できます。たとえば、以下の UntitledDocument サブクラスは、常に "[Untitled]" という名前が付けられ、それが初期化中に、そのスーパークラスから失敗可能な init(name:) イニシャライザを使用します。


  1. class UntitledDocument: Document {
  2.         override init() {
  3.                 super.init(name: "[Untitled]")!
  4.         }
  5. }

この場合、スーパークラスの init(name:) イニシャライザは、これまで名前として空の文字列と共に呼び出された場合、強制的な開封操作は実行時エラーになりました。しかし、文字列定数と共に呼び出されていますので、イニシャライザが失敗しないことを確認でき、実行時エラーは、この場合には発生しません。


init! の失敗可能イニシャライザ


通常は、init のキーワードの後に疑問符を書くことで、適切な型の optional インスタンスを作成し、失敗可能イニシャライザの定義 (init?)をします。あるいは、失敗可能イニシャライザを定義して、適切な型の暗黙的に開封された optional インスタンスを作成できます。init のキーワードの後に、疑問符の代わりに感嘆符を書くことによってこれを行います (init!)。


init? から init! にデリゲートすることができ、またその逆もでき、そして init!init? をオーバーライドすることができ、その逆もできます。また、init から init! にデリゲートすることもできますが、そうすると init! イニシャライザが、初期化に失敗するとアサーションを引き起こします。



必須イニシャライザ


クラスのすべてのサブクラスは、そのイニシャライザを実装しなければならないことを示すために、クラスイニシャライザの定義の前に required (必須) の修飾語を書いて下さい:


  1. class SomeClass {
  2.         required init() {
  3.                 // initializer implementation goes here
  4.         }
  5. }


また、イニシャライザの必要性が連鎖の中ででさらなるサブクラスに適用されることを示すために、必須イニシャライザの全てのサブクラス実装の前に required 修飾子を書かなければなりません。必須の指定イニシャライザをオーバーライドするときには、override 修飾子を書いてはいけません。


  1. class SomeSubclass: SomeClass {
  2.         required init() {
  3.                 // subclass implementation of the required initializer goes here
  4.         }
  5. }


注意: 継承されたイニシャライザで必要性を満たすことができる場合には、必須イニシャライザの明示的な実装を提供する必要はありません。



クロージャや関数でのデフォルトのプロパティ値設定


格納されたプロパティのデフォルト値が、いくつかのカスタム化や設定が必要な場合は、そのプロパティのカスタム化されたデフォルト値を提供するために、クロージャまたはグローバル関数を使用できます。プロパティが属する型の新しいインスタンスを初期化する時はいつでも、クロージャまたは関数が呼び出され、その戻り値は、プロパティのデフォルト値として代入されます。


これらの種類のクロージャや関数は、一般的にプロパティと同じ型の一時的な値を作成し、望みの初期状態を表わすためにその値を仕立て、その後プロパティのデフォルト値として使用されるべくその一時的な値を戻します。


ここで、クロージャがデフォルトのプロパティ値を提供するために使用される方法の骨格のアウトラインを挙げます。


  1. class SomeClass {
  2.         let someProperty: SomeType = {
  3.                 // create a default value for someProperty inside this closure
  4.                 // someValue must be of the same type as SomeType
  5.                 return someValue
  6.                 }()
  7. }


クロージャの終わりの中括弧に、括弧の空のペアが続いていることに注意してください。これは Swift にすぐにクロージャを実行するように指示します。これらの括弧を省略した場合、クロージャの戻り値ではなく、プロパティにクロージャ自体を代入しようとあなたはしています。


注意: プロパティを初期化するためにクロージャを使用する場合は、インスタンスの残りの部分はクロージャが実行された時点ではまだ初期化されていないことを覚えておいてください。これは、これらのプロパティがデフォルト値をたとえ持っていても、クロージャ内から他のどのプロパティ値にもアクセスできないことを意味します。また、暗黙の self プロパティを使用したり、またはインスタンスメソッドのどれも呼び出すことはできません。


以下の例は、チェスのゲームのボードをモデルにした、Chessboard と言う構造体を定義します。チェスは、黒と白の正方形を交互にした、8×8 のボード上でプレイされます。


chessBoard_2x


このゲームボードを表すには、Chessboard の構造体は、64 の Bool 値の配列である boardColors と言う一つのプロパティを持っています。配列内の値が true は、黒い正方形を表し、値が false は、白い四角を表します。配列内の最初の項目は、ボードの一番左上の正方形を表し、配列内の最後の項目のは、ボード上の右下の正方形を表します。


boardColors 配列は、その色の値を設定するためのクロージャで初期化されます:


  1. struct Chessboard {
  2.         let boardColors: [Bool] = {
  3.                 var temporaryBoard = [Bool]()
  4.                 var isBlack = false
  5.                 for i in 1...8 {
  6.                         for j in 1...8 {
  7.                         temporaryBoard.append(isBlack)
  8.                         isBlack = !isBlack
  9.                         }
  10.                         isBlack = !isBlack
  11.                 }
  12.                 return temporaryBoard
  13.                 }()
  14.         func squareIsBlackAt(row: Int, column: Int) -> Bool {
  15.                 return boardColors[(row * 8) + column]
  16.         }
  17. }


新しい Chessboard インスタンスが作成されるたびに、クロージャが実行され、boardColors のデフォルト値が計算され、戻されます。上記の例のクロージャは temporaryBoard と言う一時的な配列で、ボード上の各正方形の適切な色を計算し、設定し、その設定が完了したら、クロージャの戻り値として、この一時的な配列を戻します。戻された配列の値は boardColors に格納され、squareIsBlackAt(row:column:) ユーティリティ関数で照会される事ができます。


  1. let board = Checkerboard()
  2. print(board.squareIsBlackAt(row:0, column: 1))
  3. // prints "true"
  4. print(board.squareIsBlackAt(row:7, column: 7))
  5. // prints "false"


前:継承 次:デイニシャライザ

<BETA SOFTWARE>
このドキュメントには、開発中の API または技術に関する予備的な情報が含まれています。この情報は変更されることがあり、このドキュメントに従って実装されたソフトウェアは、最終的なオペレーティングシステムソフトウェアでテストする必要があります。

Apple の Beta ソフトウエアについての詳細






目次
Xcode 10 の新機能

Swift について
Swift と Cocoa と Objective-C (obsolete)
Swift Blog より (obsolete)

SwiftLogo
  • Swift 4.2 全メニュー


  • Swift へようこそ
  • Swift について
  • Swift 言語のガイド
  • Swift の基本
  • 基本演算子
  • 文字列と文字
  • コレクション型
  • フロー制御
  • 関数
  • クロージャ
  • 列挙型
  • クラスと構造体
  • プロパティ
  • メソッド
  • サブスクリプト
  • 継承
  • 初期化
  • デイニシャライザ
  • Optional の連鎖
  • エラー処理
  • 型キャスト
  • ネストした型
  • 拡張機能
  • プロトコル
  • ジェネリック(汎用)
  • 自動参照カウント
  • メモリの安全性
  • アクセス制御
  • 高度な演算子

  • 言語リファレンス

  • マニュアルの変更履歴













  • トップへ












    トップへ












    トップへ












    トップへ












    トップへ












    トップへ












    トップへ












    トップへ












    トップへ












    トップへ