Swift 初期化プロセス

初期化プロセスは、クラス、構造体、または列挙型のインスタンスを使用するための準備プロセスです。このプロセスには、インスタンス内の各プロパティに初期値を設定し、必要な準備と初期化タスクを実行することが含まれます。

Swift では、イニシャライザとして init() メソッドを使用します。

Objective-C のイニシャライザとは異なり、Swift のイニシャライザは戻り値を必要としません。それらの主な役割は、新しいインスタンスが初めて使用される前に正しい初期化を完了していることを保証することです。

クラスインスタンスは、デイニシャライザを定義することで、クラスインスタンスが解放される前にメモリのクリーンアップ作業を実行することもできます。


ストアドプロパティの初期値の設定

クラスと構造体は、インスタンス作成時に、すべてのストアドプロパティに適切な初期値を設定する必要があります。

ストアドプロパティはイニシャライザ内で値が代入される場合、値は直接設定され、プロパティオブザーバは一切トリガーされません。

ストアドプロパティをイニシャライザ内で代入する流れ:

  • 初期値を作成する。

  • プロパティ定義でデフォルトのプロパティ値を指定する。

  • インスタンスを初期化し、init() メソッドを呼び出す。


イニシャライザ

イニシャライザは、特定の型の新しいインスタンスを作成するときに呼び出されます。その最も単純な形式は、引数を取らないインスタンスメソッドに似ており、キーワード init で名前が付けられています。

構文

init()
{
    // 实例化后执行的代码
}

例

次の構造体は、引数を取らないイニシャライザ init を定義し、その中でストアドプロパティ length と breadth の値を 6 と 12 に初期化しています:

struct rectangle {
    var length: Double
    var breadth: Double
    init() {
        length = 6
        breadth = 12
    }
}
var area = rectangle()
print("矩形面积为 \(area.length*area.breadth)")

上記プログラムの実行出力結果は次のとおりです:

矩形面积为 72.0

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

イニシャライザ内でストアドプロパティに初期値を設定できます。同様に、プロパティ宣言時にデフォルト値を設定することもできます。

デフォルト値を使用すると、イニシャライザをより簡潔で明確にすることができ、デフォルト値からプロパティの型を自動的に推論することもできます。

次の例では、プロパティ宣言時にデフォルト値を設定しています:

struct rectangle {
    // 设置默认值
    var length = 6
    var breadth = 12
}
var area = rectangle()
print("矩形的面积为 \(area.length*area.breadth)")

上記プログラムの実行出力結果は次のとおりです:

矩形面积为 72

イニシャライザの引数

イニシャライザ init() を定義するときに、次のように引数を指定できます:

struct Rectangle {
    var length: Double
    var breadth: Double
    var area: Double
    
    init(fromLength length: Double, fromBreadth breadth: Double) {
        self.length = length
        self.breadth = breadth
        area = length * breadth
    }
    
    init(fromLeng leng: Double, fromBread bread: Double) {
        self.length = leng
        self.breadth = bread
        area = leng * bread
    }
}

let ar = Rectangle(fromLength: 6, fromBreadth: 12)
print("面积为: \(ar.area)")

let are = Rectangle(fromLeng: 36, fromBread: 12)
print("面积为: \(are.area)")

上記プログラムの実行出力結果は次のとおりです:

面积为: 72.0
面积为: 432.0

内部引数名と外部引数名

関数やメソッドの引数と同様に、イニシャライザの引数にも、イニシャライザ内部で使用される引数名と、イニシャライザを呼び出すときに使用される外部引数名があります。

しかし、イニシャライザは関数やメソッドのように括弧の前に識別可能な名前を持ちません。そのため、イニシャライザを呼び出すときは、主にイニシャライザ内の引数名と型によって呼び出すイニシャライザを決定します。

イニシャライザを定義する際に引数の外部名を指定しない場合、Swift は各イニシャライザの引数に対して、内部名と同じ外部名を自動的に生成します。

struct Color {
    let red, green, blue: Double
    init(red: Double, green: Double, blue: Double) {
        self.red   = red
        self.green = green
        self.blue  = blue
    }
    init(white: Double) {
        red   = white
        green = white
        blue  = white
    }
}

// 创建一个新的Color实例,通过三种颜色的外部参数名来传值,并调用构造器
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)

print("red 值为: \(magenta.red)")
print("green 值为: \(magenta.green)")
print("blue 值为: \(magenta.blue)")

// 创建一个新的Color实例,通过三种颜色的外部参数名来传值,并调用构造器
let halfGray = Color(white: 0.5)
print("red 值为: \(halfGray.red)")
print("green 值为: \(halfGray.green)")
print("blue 值为: \(halfGray.blue)")

上記プログラムの実行出力結果は次のとおりです:

red 值为: 1.0
green 值为: 0.0
blue 值为: 1.0
red 值为: 0.5
green 值为: 0.5
blue 值为: 0.5

外部名なしの引数

イニシャライザの引数に外部名を提供したくない場合は、アンダースコアを使用できます_外部名を明示的に記述するために使用します。

struct Rectangle {
    var length: Double
    
    init(frombreadth breadth: Double) {
        length = breadth * 10
    }
    
    init(frombre bre: Double) {
        length = bre * 30
    }
    //不提供外部名字
    init(_ area: Double) {
        length = area
    }
}

// 调用不提供外部名字
let rectarea = Rectangle(180.0)
print("面积为: \(rectarea.length)")

// 调用不提供外部名字
let rearea = Rectangle(370.0)
print("面积为: \(rearea.length)")

// 调用不提供外部名字
let recarea = Rectangle(110.0)
print("面积为: \(recarea.length)")

上記プログラムの実行出力結果は次のとおりです:

面积为: 180.0
面积为: 370.0
面积为: 110.0

オプショナル型プロパティ

カスタム型に、論理的に空の値を許容するストアドプロパティが含まれる場合は、それをオプショナル型(オプショナルプロパティ型)として定義する必要があります。

ストアドプロパティがオプショナルとして宣言されると、自動的に nil に初期化されます。

struct Rectangle {
    var length: Double?
    
    init(frombreadth breadth: Double) {
        length = breadth * 10
    }
    
    init(frombre bre: Double) {
        length = bre * 30
    }
    
    init(_ area: Double) {
        length = area
    }
}

let rectarea = Rectangle(180.0)
print("面积为:\(rectarea.length)")

let rearea = Rectangle(370.0)
print("面积为:\(rearea.length)")

let recarea = Rectangle(110.0)
print("面积为:\(recarea.length)")

上記プログラムの実行出力結果は次のとおりです:

面积为:Optional(180.0)
面积为:Optional(370.0)
面积为:Optional(110.0)

初期化中に定数プロパティを変更する

初期化プロセスが終了する前に定数の値を確定できるのであれば、初期化プロセス中の任意の時点で定数プロパティの値を変更できます。

クラスインスタンスの場合、その定数プロパティは、定義元のクラスの初期化プロセス内でのみ変更できます。サブクラスでは変更できません。

length プロパティは現在定数ですが、そのクラスのイニシャライザ内でその値を設定することは依然としてできます:

struct Rectangle {
    let length: Double?
    
    init(frombreadth breadth: Double) {
        length = breadth * 10
    }
    
    init(frombre bre: Double) {
        length = bre * 30
    }
    
    init(_ area: Double) {
        length = area
    }
}

let rectarea = Rectangle(180.0)
print("面积为:\(rectarea.length)")

let rearea = Rectangle(370.0)
print("面积为:\(rearea.length)")

let recarea = Rectangle(110.0)
print("面积为:\(recarea.length)")

上記プログラムの実行出力結果は次のとおりです:

面积为:Optional(180.0)
面积为:Optional(370.0)
面积为:Optional(110.0)

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

デフォルトイニシャライザは、すべてのプロパティ値がデフォルト値に設定されたインスタンスを単純に作成します:

次の例では、ShoppingListItem クラスのすべてのプロパティにデフォルト値があり、このクラスは親クラスを持たない基底クラスであるため、すべてのプロパティにデフォルト値を設定できるデフォルトイニシャライザを自動的に取得します。

class ShoppingListItem {
    var name: String?
    var quantity = 1
    var purchased = false
}
var item = ShoppingListItem()


print("名字为: \(item.name)")
print("数理为: \(item.quantity)")
print("是否付款: \(item.purchased)")

上記プログラムの実行出力結果は次のとおりです:

名字为: nil
数理为: 1
是否付款: false

構造体のメンバーワイズイニシャライザ

構造体がすべてのストアドプロパティにデフォルト値を提供し、独自のカスタムイニシャライザを提供しない場合、自動的にメンバーワイズイニシャライザを取得できます。

メンバーワイズイニシャライザを呼び出すときは、メンバープロパティ名と同じ引数名で値を渡すことで、メンバープロパティの初期値を設定します。

次の例では、2つのプロパティ length と breadth を含む構造体 Rectangle を定義しています。Swift は、これら2つのプロパティへの初期値 100.0、200.0 から、それらの型が Double であることを自動的に推論できます。

struct Rectangle {
    var length = 100.0, breadth = 200.0
}
let area = Rectangle(length: 24.0, breadth: 32.0)

print("矩形的面积: \(area.length)")
print("矩形的面积: \(area.breadth)")

これら2つのストアドプロパティにはデフォルト値があるため、構造体 Rectangle はメンバーワイズイニシャライザ init(width:height:) を自動的に取得します。これを使用して Rectangle の新しいインスタンスを作成できます。

上記プログラムの実行出力結果は次のとおりです:

矩形的面积: 24.0
矩形的面积: 32.0

値型のイニシャライザ委譲

イニシャライザは、他のイニシャライザを呼び出すことで、インスタンスの初期化プロセスの一部を完了できます。このプロセスをイニシャライザ委譲と呼び、複数のイニシャライザ間のコードの重複を減らすことができます。

次の例では、Rect 構造体が Size と Point の初期化プロセスを呼び出しています:

struct Size {
    var width = 0.0, height = 0.0
}
struct Point {
    var x = 0.0, y = 0.0
}

struct Rect {
    var origin = Point()
    var size = Size()
    init() {}
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}


// origin和size属性都使用定义时的默认值Point(x: 0.0, y: 0.0)和Size(width: 0.0, height: 0.0):
let basicRect = Rect()
print("Size 结构体初始值: \(basicRect.size.width, basicRect.size.height) ")
print("Rect 结构体初始值: \(basicRect.origin.x, basicRect.origin.y) ")

// 将origin和size的参数值赋给对应的存储型属性
let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
    size: Size(width: 5.0, height: 5.0))

print("Size 结构体初始值: \(originRect.size.width, originRect.size.height) ")
print("Rect 结构体初始值: \(originRect.origin.x, originRect.origin.y) ")


//先通过center和size的值计算出origin的坐标。
//然后再调用(或代理给)init(origin:size:)构造器来将新的origin和size值赋值到对应的属性中
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
    size: Size(width: 3.0, height: 3.0))

print("Size 结构体初始值: \(centerRect.size.width, centerRect.size.height) ")
print("Rect 结构体初始值: \(centerRect.origin.x, centerRect.origin.y) ")

上記プログラムの実行出力結果は次のとおりです:

Size 结构体初始值: (0.0, 0.0) 
Rect 结构体初始值: (0.0, 0.0) 
Size 结构体初始值: (5.0, 5.0) 
Rect 结构体初始值: (2.0, 2.0) 
Size 结构体初始值: (3.0, 3.0) 
Rect 结构体初始值: (2.5, 2.5) 

イニシャライザ委譲の規則

値型 クラス型
値型は継承をサポートしないため、イニシャライザ委譲のプロセスは比較的単純です。それらは自身が提供する他のイニシャライザにのみ委譲できます。カスタムイニシャライザ内で self.init を使用して、同じ値型に属する他のイニシャライザを参照できます。 クラス型は他のクラスから継承できるため、クラスは継承したすべてのストアドプロパティが初期化時に正しく初期化されることを保証する責任があります。

クラスの継承と初期化プロセス

Swift は、すべてのクラスインスタンスのストアドプロパティが初期値を取得できるようにするために、2種類のクラスイニシャライザを提供しています。それらは指定イニシャライザとコンビニエンスイニシャライザです。

指定イニシャライザ コンビニエンスイニシャライザ
クラスの中で最も主要なイニシャライザ クラスの中で比較的補助的な役割を担うイニシャライザ
クラス内のすべてのプロパティを初期化し、親クラスのチェーンに従って親クラスのイニシャライザを呼び出し、親クラスの初期化を実現します。 コンビニエンスイニシャライザは、同じクラス内の指定イニシャライザを呼び出し、その引数にデフォルト値を提供するように定義できます。また、特定の用途や特定の入力を持つインスタンスを作成するためにコンビニエンスイニシャライザを定義することもできます。
すべてのクラスは少なくとも1つの指定イニシャライザを持たなければなりません。 必要な場合にのみ、クラスにコンビニエンスイニシャライザを提供します。
Init(parameters) {
    statements
}
convenience init(parameters) {
      statements
}

指定イニシャライザの例

class mainClass {
    var no1 : Int // 局部存储变量
    init(no1 : Int) {
        self.no1 = no1 // 初始化
    }
}
class subClass : mainClass {
    var no2 : Int // 新的子类存储变量
    init(no1 : Int, no2 : Int) {
        self.no2 = no2 // 初始化
        super.init(no1:no1) // 初始化超类
    }
}

let res = mainClass(no1: 10)
let res2 = subClass(no1: 10, no2: 20)

print("res 为: \(res.no1)")
print("res2 为: \(res2.no1)")
print("res2 为: \(res2.no2)")

上記プログラムの実行出力結果は次のとおりです:

res 为: 10
res 为: 10
res 为: 20

コンビニエンスイニシャライザの例

class mainClass {
    var no1 : Int // 局部存储变量
    init(no1 : Int) {
        self.no1 = no1 // 初始化
    }
}

class subClass : mainClass {
    var no2 : Int
    init(no1 : Int, no2 : Int) {
        self.no2 = no2
        super.init(no1:no1)
    }
    // 便利方法只需要一个参数
    override convenience init(no1: Int)  {
        self.init(no1:no1, no2:0)
    }
}
let res = mainClass(no1: 20)
let res2 = subClass(no1: 30, no2: 50)

print("res 为: \(res.no1)")
print("res2 为: \(res2.no1)")
print("res2 为: \(res2.no2)")

上記プログラムの実行出力結果は次のとおりです:

res 为: 20
res2 为: 30
res2 为: 50

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

Swift のサブクラスは、親クラスのイニシャライザをデフォルトでは継承しません。

親クラスのイニシャライザは、確実で安全な場合にのみ継承されます。

親クラスの指定イニシャライザをオーバーライドする場合、override 修飾子を記述する必要があります。

class SuperClass {
    var corners = 4
    var description: String {
        return "\(corners) 边"
    }
}
let rectangle = SuperClass()
print("矩形: \(rectangle.description)")

class SubClass: SuperClass {
    override init() {  //重载构造器
        super.init()
        corners = 5
    }
}

let subClass = SubClass()
print("五角型: \(subClass.description)")

上記プログラムの実行出力結果は次のとおりです:

矩形: 4 边
五角型: 5 边

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

次の例では、指定イニシャライザ、コンビニエンスイニシャライザ、および自動イニシャライザの継承を実際に示します。

この例は、MainClass と SubClass の2つのクラスを含むクラス階層を定義し、それらのイニシャライザがどのように相互作用するかを示します。

class MainClass {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    convenience init() {
        self.init(name: "[匿名]")
    }
}
let main = MainClass(name: "サンプル")
print("MainClass 名字为: \(main.name)")

let main2 = MainClass()
print("没有对应名字: \(main2.name)")

class SubClass: MainClass {
    var count: Int
    init(name: String, count: Int) {
        self.count = count
        super.init(name: name)
    }
    
    override convenience init(name: String) {
        self.init(name: name, count: 1)
    }
}

let sub = SubClass(name: "サンプル")
print("MainClass 名字为: \(sub.name)")

let sub2 = SubClass(name: "サンプル", count: 3)
print("count 变量: \(sub2.count)")

上記プログラムの実行出力結果は次のとおりです:

MainClass 名字为: サンプル
没有对应名字: [匿名]
MainClass 名字为: サンプル
count 变量: 3

クラスの失敗可能イニシャライザ

クラス、構造体、または列挙型のオブジェクトが、自身の初期化プロセス中に失敗する可能性がある場合は、その型に失敗可能イニシャライザを定義します。

変数の初期化が失敗する可能性がある理由は次のとおりです:

  • 無効な引数値が渡された。

  • 必要な外部リソースが不足しています。

  • 特定の条件を満たしていません。

このような構築プロセスで失敗する可能性がある状況に適切に対処するために。

クラス、構造体、または列挙型の定義に、1つ以上の失敗可能イニシャライザを追加できます。その構文は、initキーワードの後に疑問符を追加します(init?)。

例

次の例では、Animalという名前の構造体を定義しています。その中に、speciesという名前の、String型の定数プロパティがあります。

また、この構造体には、String型のパラメータspeciesを持つ失敗可能イニシャライザも定義されています。この失敗可能イニシャライザは、渡されたパラメータが空文字列かどうかをチェックするために使用されます。空文字列の場合は、この失敗可能イニシャライザはオブジェクトの構築に失敗し、それ以外の場合は成功します。

struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}

//通过该可失败构造器来构建一个Animal的对象,并检查其构建过程是否成功
// someCreature 的类型是 Animal? 而不是 Animal
let someCreature = Animal(species: "长颈鹿")

// 打印 "动物初始化为长颈鹿"
if let giraffe = someCreature {
    print("动物初始化为\(giraffe.species)")
}

上記のプログラムを実行すると、出力結果は次のようになります。

動物はキリンに初期化される

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

1つ以上のパラメータを持つ失敗可能イニシャライザを構築することで、列挙型内の特定の列挙メンバーを取得できます。

例

次の例では、TemperatureUnitという名前の列挙型を定義しています。これには3つの可能な列挙メンバー(Kelvin、Celsius、Fahrenheit)と、Character値に対応する列挙メンバーを見つけるために使用される失敗可能イニシャライザが含まれています:

enum TemperatureUnit {
    // 开尔文,摄氏,华氏
    case Kelvin, Celsius, Fahrenheit
    init?(symbol: Character) {
        switch symbol {
        case "K":
            self = .Kelvin
        case "C":
            self = .Celsius
        case "F":
            self = .Fahrenheit
        default:
            return nil
        }
    }
}


let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
    print("这是一个已定义的温度单位,所以初始化成功。")
}

let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
    print("这不是一个已定义的温度单位,所以初始化失败。")
}

上記のプログラムを実行すると、出力結果は次のようになります。

これは定義済みの温度単位なので、初期化は成功します。  
これは定義済みの温度単位ではないので、初期化は失敗します。

クラスの失敗可能イニシャライザ

値型(構造体や列挙型など)の失敗可能イニシャライザは、いつどこで初期化失敗が発生するかについて、何の制限もありません。

ただし、クラスの失敗可能イニシャライザは、すべてのクラスプロパティが初期化され、すべてのクラス間のイニシャライザの委任呼び出しが完了した後にのみ、失敗をトリガーできます。

例

次の例では、StudRecordという名前のクラスを定義しています。studnameプロパティは定数であるため、StudRecordクラスの構築が成功すると、studnameプロパティは必ずnilではない値を持ちます。

class StudRecord {
    let studname: String!
    init?(studname: String) {
        self.studname = studname
        if studname.isEmpty { return nil }
    }
}
if let stname = StudRecord(studname: "失败构造器") {
    print("模块为 \(stname.studname)")
}

上記のプログラムを実行すると、出力結果は次のようになります。

モジュールは 失敗コンストラクタ

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

他のイニシャライザと同様に、サブクラスの失敗可能イニシャライザで基底クラスの失敗可能イニシャライザをオーバーライドすることもできます。

または、サブクラスの非失敗可能イニシャライザで基底クラスの失敗可能イニシャライザをオーバーライドすることもできます。

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

非失敗可能イニシャライザは、失敗可能イニシャライザを委任呼び出しすることは決してできません。

例

以下の例は、失敗可能イニシャライザと非失敗可能イニシャライザを示しています:

class Planet {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    convenience init() {
        self.init(name: "[No Planets]")
    }
}
let plName = Planet(name: "Mercury")
print("行星的名字是: \(plName.name)")

let noplName = Planet()
print("没有这个名字的行星: \(noplName.name)")

class planets: Planet {
    var count: Int
    
    init(name: String, count: Int) {
        self.count = count
        super.init(name: name)
    }
    
    override convenience init(name: String) {
        self.init(name: name, count: 1)
    }
}

上記のプログラムを実行すると、出力結果は次のようになります。

行星的名字是: Mercury
没有这个名字的行星: [No Planets]

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

通常、initキーワードの後に疑問符を追加する方法(init?)で失敗可能イニシャライザを定義しますが、initの後に感嘆符を追加する方法で失敗可能イニシャライザ(init!)を定義することもできます。例は次のとおりです:

struct StudRecord {
    let stname: String
    
    init!(stname: String) {
        if stname.isEmpty {return nil }
        self.stname = stname
    }
}

let stmark = StudRecord(stname: "サンプル")
if let name = stmark {
    print("指定了学生名")
}

let blankname = StudRecord(stname: "")
if blankname == nil {
    print("学生名为空")
}

上記のプログラムを実行すると、出力結果は次のようになります。

学生名を指定しました  
学生名が空です
その他の拡張