CGImage から画素データにアクセス

        let cgimage:CGImage
        let data = cgimage.dataProvider?.data
        let bytes = CFDataGetBytePtr(data)!
        
        let bytesPerPixel = cgimage.bitsPerPixel / cgimage.bitsPerComponent

        for y in 0 ..< cgimage.height {
            for x in 0 ..< cgimage.width {
                    let offset = (y * cgimage.bytesPerRow) + (x * bytesPerPixel)
                    let b = bytes[offset]
                    let g = bytes[offset + 1]
                    let r = bytes[offset + 2]
                }
            }

配列、多次元配列、セット

        let nums1D = [1, 2, 3, 4, 5, 6, 7, 8]
        var array1D1: Array<Int>           // 宣言のみ
        var array1D2 = Array<Int>()        // 空の配列 その1
        var array1D3 = [Int]()             // 空の配列 省略型 その2
        var array1D4: [Int] = []           // 空の配列 省略型 その3

        // 二次元配列
        let nums2D = [[1, 2, 3, 4], [5, 6, 7, 8]]
        var array2D1: Array<Array<Int>>    // 宣言のみ
        var array2D2: Array<[Int]>         // 宣言のみ 省略型 その1
        var array2D3: [[Int]]              // 宣言のみ 省略型 その2
        var array2D4 = Array<Array<Int>>() // 空の配列 その1
        var array2D5 = [[Int]]()           // 空の配列 省略型 その2
        var array2D6: [[Int]] = [[]]       // 空の配列 省略型 その3

map

        let array: [Int] = [1, 2, 3, 4, 5]

        let array1 = array.map{i in  i * 2 }
        print(array1)

        let array2 = array.map{$0 * 2}           // クロージャーによる表記方
        print(array2)

セット

        let set1: Set = [1, 2, 3]
        print(set1)   // 結果: [2, 3, 1] 順番は保証されない
        

        let set2: Set = [1, 2, 3, 2, 3]
        print(set2)  // 結果:  [1, 3, 2] 順番は保証されない。同じ値を持てない(ユニーク)

UISlider が操作できない

Vertical Stack に UISlider を2つ追加してみたところ、操作ができない。

UIText を追加すると、操作ができるようになる。

原因がわからなかったが、レイアウト情報を与えていなかったのが原因のようだ。

Add Missing Constraints で不足しているレイアウト位置情報を与えてあげると、操作ができるようになった。

Swift ViewController で Fatal error

Swift のViewControllerでデザインしていて、コントロールを追加したり、削除したりしていると、時々次のようなエラーが発生する。

Thread : Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

これは、コントロールと、outputやaction の接続がおかしくなった場合に発生することがある。

対応としては、 outputやaction を削除して、もう一度作り直す。

CIImage でフィルタリング

extension CIImage {
    func invert() -> CIImage? {
        guard let invertFilter = CIFilter(name: "CIColorInvert") else {return nil}

        invertFilter.setValue(self, forKey: kCIInputImageKey)

        return  invertFilter.outputImage
    }

    func convert(saturation: Double, bright: Double, contrast: Double) -> CIImage? {
        guard let filter1 = CIFilter(name: "CIColorControls") else {return nil}

        filter1.setValue(self, forKey: kCIInputImageKey)
        filter1.setValue(saturation, forKey: "inputSaturation")
        filter1.setValue(bright, forKey: "inputBrightness")
        filter1.setValue(contrast, forKey: "inputContrast")

        return filter1.outputImage
        }
    
    func temperatureAndTint(inTemp: CGFloat, outTemp: CGFloat) -> CIImage? {
        guard let filter = CIFilter(name: "CITemperatureAndTint") else {return nil}
        filter.setValue(self, forKey: kCIInputImageKey)
        filter.setValue(CIVector(x: inTemp, y: 0), forKey: "inputNeutral")
        filter.setValue(CIVector(x: outTemp, y: 0), forKey: "inputTargetNeutral")
        return filter.outputImage
    }
    
    func rgbaMatrix() -> CIImage?
    {
        guard let filter = CIFilter(name: "CIColorMatrix") else {return nil}
        let ciImage = self
        filter.setValue(ciImage, forKey: kCIInputImageKey)
        filter.setValue(CIVector(x: 1.0, y: 0, z: 0, w: 0.0), forKey: "inputRVector")
        filter.setValue(CIVector(x: 0, y: 1.0, z: 0, w: 0.0), forKey: "inputGVector")
        filter.setValue(CIVector(x: 0, y: 0, z: 1.0, w: 0.0), forKey: "inputBVector")
        filter.setValue(CIVector(x: 0, y: 0, z: 0, w: 1), forKey: "inputAVector")
        filter.setValue(CIVector(x: 0, y: 0, z: 0, w: 0), forKey: "inputBiasVector")
        return filter.outputImage
    }
}

オプショナルの連鎖

オプショナルの連鎖は、現在nilが存在している可能性があるオプショナルプロパティ、メソッド、およびサブスクリプトを照会および呼び出すためのプロセスです。オプショナルに値が含まれている場合、プロパティ、メソッド、またはサブスクリプトの呼び出しは成功します。オプショナルがnilの場合、プロパティ、メソッド、またはサブスクリプトの呼び出しはnilを返します。複数のクエリを連鎖することができ、連鎖内のリンクがnilである場合、連鎖全体が正常に失敗します。

注意

Swiftでのオプショナルの連鎖はObjective-Cでのnilメッセージングに似ていますが、どのタイプでも機能し、成功または失敗を確認できるようになっています。

強制アンラップの代替としてのオプショナルの連鎖

オプショナルがnil以外の場合、プロパティ、メソッド、またはサブスクリプトを呼び出すオプショナル値の後に疑問符(?)を配置して、オプショナル連鎖を指定します。これは、オプショナル値の後に感嘆符(!)を配置して、その値を強制的にアンラップするのとよく似ています。主な違いは、オプショナルがnilの場合はオプショナルの連鎖が正常に失敗するのに対し、オプショナルがnilの場合は強制アンラップが実行時エラーをトリガーすることです。

nil値に対してオプショナルの連鎖を呼び出すことができるという事実を反映するために、オプショナルの連鎖呼び出しの結果は、クエリしているプロパティ、メソッド、またはサブスクリプトが非オプショナル値を返した場合でも、常にオプショナル値になります。このオプショナル戻り値を使用して、オプショナルの連鎖呼び出しが成功した(返されたオプショナルに値が含まれている)かnil、連鎖内の値が原因で成功しなかったか(返されたオプショナル値はnil)を確認できます。

具体的には、オプショナルの連鎖呼び出しの結果は、予期される戻り値と同じ型ですが、オプショナルでラップされます。通常はIntを返すプロパティは、オプショナルの連鎖を通じてアクセスするとInt?を返します。

次のいくつかのコードスニペットは、オプショナル連鎖が強制アンラップとどのように異なるかを示し、成功を確認できるようにします。

まず、2つのクラス Person とResidence を定義します。

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}

ResidenceインスタンスにはnumberOfRoomsと呼ばれる単一のIntプロパティがあり、デフォルト値は1です。Personインスタンスには、Residence?型のオプショナルプロパティresidenceがあります。

新しいPersonインスタンスを作成する場合、そのresidenceプロパティはオプショナルであるため、デフォルトでnilに初期化されます。以下のコードでjohnのresidenceプロパティ値はnilになります。

let john = Person()

この人物 residence のnumberOfRoomsのプロパティにアクセスしようとすると、residenceの後に感嘆符を配置してその値を強制的にアンラップすると、アンラップするresidence値がないため、ランタイムエラーが発生します。

let roomCount = john.residence!.numberOfRooms
// this triggers a runtime error

上記のコードjohn.residenceは、非nil値の場合に成功し、roomCountを適切な数の部屋を含むInt値に設定します。ただし、上記のように、このコードは常にresidenceがnilのときにランタイムエラーをトリガーします。

オプショナルの連鎖は、numberOfRoomsの値にアクセスするための代替方法を提供します。オプショナル連鎖を使用するには、感嘆符の代わりに疑問符を使用します。

if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
// Prints "Unable to retrieve the number of rooms."

これは、Swiftにオプショナルresidenceプロパティを「連鎖」し、residencegが存在する場合はnumberOfRoomsの値を取得するように指示します。

numberOfRoomsへのアクセスの試みは失敗する可能性があるため、オプショナル連鎖の試みはInt?型の値または「optional Int」を返します。residenceがnilのとき、上記の例のように、それがnumberOfRoomsにアクセスすることができなかったという事実を反映して、このオプショナルIntはnilになります。オプショナルIntは、オプショナルバインディングを介してアクセスされ、整数をアンラップして、非オプショナルの値をroomCount変数に割り当てます。

これはnumberOfRoomsが非オプショナルIntでも、これは事実です。オオプショナルの連鎖を通じて照会されるという事実は、numberOfRoomsへの呼び出しが常にのInt?代わりにIntを返すことを意味します。

Residenceインスタンスをjohn.residenceに割り当てると、nil値ではなくなります。

john.residence = Residence()

ここで、john.residenceはnilではなく、実際のResidenceインスタンスが含んでています。以前と同じオプショナルの連鎖を使用してnumberOfRoomsをアクセスしようとすると、numberOfRoomsのデフォルト値1の Int? が返されます。

if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
// Prints "John's residence has 1 room(s)."

オプショナル連鎖のためのモデルクラスの定義

複数レベルの深さのプロパティ、メソッド、およびサブスクリプトの呼び出しでオプショナルの連鎖を使用できます。これにより、相互に関連するタイプの複雑なモデル内のサブプロパティにドリルダウンし、それらのサブプロパティのプロパティ、メソッド、およびサブスクリプトにアクセスできるかどうかを確認できます。

以下のコードスニペットは、マルチレベルのオプショナル連鎖の例を含む、後続のいくつかの例で使用する4つのモデルクラスを定義します。これらのクラスは、関連するプロパティ、メソッド、およびサブスクリプトとともに、RoomおよびAddressクラスを追加することにより、上記のPersonおよびResidenceモデルを拡張します。

Personクラスは、前と同じように定義されています。

class Person {
    var residence: Residence?
}

Residenceクラスは、以前よりもより複雑です。今回は、Residenceクラスがroomsと呼ばれる変数プロパティを定義します。これは、[Room]型の空の配列で初期化されます。

class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        get {
            return rooms[i]
        }
        set {
            rooms[i] = newValue
        }
    }
    func printNumberOfRooms() {
        print("The number of rooms is \(numberOfRooms)")
    }
    var address: Address?
}

このバージョンのResidenceRoomインスタンスの配列を格納するため、そのnumberOfRoomsプロパティは、格納プロパティではなく、計算プロパティとして実装されます。計算プロパティnumberOfRoomsは、単にrooms配列からcountプロパティの値を返します。

rooms配列にアクセスするためのショートカットとして、このResidenceバージョンのは、rooms配列内の要求されたインデックスにある部屋へのアクセスを提供する読み書きサブスクリプトが用意されています。

このバージョンのResidenceには、printNumberOfRoomsと呼ばれるメソッドも用意されています。これは、住宅の部屋数を出力するだけです。

最後に、ResidenceというAddress?型のaddressと呼ばれるオプショナルプロパティを定義します。このプロパティのAddressクラスタイプは以下に定義されています。

rooms配列のために使用されるRoomクラスは、nameプロパティを持つ単純なクラスであり、そのプロパティに適切なルーム名を設定するイニシャライザ:

class Room {
    let name: String
    init(name: String) { self.name = name }
}

このモデルの最後のクラスはAddressと呼ばれます。このクラスには、String?型の3つのオプショナルプロパティがあります。最初の2つのプロパティbuildingNamebuildingNumberは、住所の一部として特定の建物を識別するための代替方法です。3番目のプロパティstreetは、その住所の道路に名前を付けるために使用されます。

class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if let buildingNumber = buildingNumber, let street = street {
            return "\(buildingNumber) \(street)"
        } else if buildingName != nil {
            return buildingName
        } else {
            return nil
        }
    }
}

このAddressクラスは、buildingIdentifier()と呼ばれるメソッドも提供します。このメソッドの戻り型はString?です。このメソッドは、アドレスのプロパティをチェックし、値がある場合はbuildingName、または両方に値がある場合はbuildingNumberとstreetを連結したもの、そうでなければ nil を返します。

オプショナル連鎖によるプロパティへのアクセス

強制アンラップの代替としてのオプショナルの連鎖示されているように、オプショナルの連鎖使用して、オプショナル値のプロパティにアクセスし、そのプロパティアクセスが成功したかどうかを確認できます。

上記で定義したクラスを使用して新しいPersonインスタンスを作成し、以前のようにそのnumberOfRoomsプロパティにアクセスしてみます。

let john = Person()
if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
// Prints "Unable to retrieve the number of rooms."

john.residenceは nil であり、このオプショナル連鎖コールは、前と同じように失敗します。

オプショナルの連鎖を通じてプロパティの値を設定することもできます。

let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
john.residence?.address = someAddress

この例では、john.residenceは現在 nil であるため、john.residenceのaddressプロパティを設定しようとしても失敗します。

割り当てはオプショナル連鎖の一部です。つまり、= 演算子の右側のコードは評価されません。前の例では、定数にアクセスしても副作用がないため、someAddressが評価されないことが簡単にわかりません。以下のリストは同じ割り当てを行っていますが、関数を使用してアドレスを作成しています。この関数は、値を返す前に「Function was called」を出力します。これにより、=演算子の右側が評価されたかどうかを確認できます。

func createAddress() -> Address {
    print("Function was called.")

    let someAddress = Address()
    someAddress.buildingNumber = "29"
    someAddress.street = "Acacia Road"

    return someAddress
}
john.residence?.address = createAddress()

何も出力されないため、createAddress() 関数が呼び出されていないことがわかります。

オプショナル連鎖によるメソッドの呼び出し

オプショナルの連鎖を使用して、オプショナル値でメソッドを呼び出し、そのメソッドの呼び出しが成功したかどうかを確認できます。そのメソッドが戻り値を定義していない場合でも、これを行うことができます。

ResidenceクラスのprintNumberOfRooms()メソッドは、numberOfRoomsの現在の値を出力します。メソッドは次のようになります。

func printNumberOfRooms() {
    print("The number of rooms is \(numberOfRooms)")
}

このメソッドは戻り値の型を指定しません。ただし、戻り値のない関数とメソッドには、戻り値のない関数で説明されているように、暗黙的なVoid型の戻り値を持ちます。これは、()の値または空のタプルを返すことを意味します。

If you call this method on an optional value with optional chaining, the method’s return type will be Void?, not Void, because return values are always of an optional type when called through optional chaining. This enables you to use an if statement to check whether it was possible to call the printNumberOfRooms() method, even though the method does not itself define a return value. Compare the return value from the printNumberOfRooms call against nil to see if the method call was successful:

オプショナルの連鎖を使用してオプショナル値でこのメソッドを呼び出す場合、メソッドの戻り値の型はVoidではなくVoid?になります。これは、オプショナルの連鎖を通じて呼び出される場合、戻り値は常にオプショナル型であるためです。これにより、メソッド自体が戻り値を定義していなくても、ifステートメントを使用してprintNumberOfRooms()メソッドを呼び出すことができたかどうかを確認できます。printNumberOfRooms呼び出しからの戻り値をnilと比較して、メソッド呼び出しが成功したかどうかを確認します。

if john.residence?.printNumberOfRooms() != nil {
    print("It was possible to print the number of rooms.")
} else {
    print("It was not possible to print the number of rooms.")
}
// Prints "It was not possible to print the number of rooms."

オプショナルの連鎖を通じてプロパティを設定しようとした場合も同様です。上記のオプショナルの連鎖によるプロパティへのアクセスの例では、residenceプロパティがnilであってもjohn.residenceにaddress値を設定しようとしています。オプショナルの連鎖を介してプロパティを設定しようとすると、Void?型の値が返されます。これをnilで比較して、プロパティが正常に設定されたかどうかを確認できます。

if (john.residence?.address = someAddress) != nil {
    print("It was possible to set the address.")
} else {
    print("It was not possible to set the address.")
}
// Prints "It was not possible to set the address."

オプショナル連鎖によるサブスクリプトのアクセス

オプショナル連鎖を使用して、オプショナル値のサブスクリプトから値を取得および設定し、そのサブスクリプトの呼び出しが成功したかどうかを確認できます。

注意

オプショナル連鎖を介してオプショナル値のサブスクリプトにアクセスする場合、疑問符をサブスクリプトの大括弧の後ではなく前に置きます。オプショナル連鎖の疑問符は常に、オプショナルの式の部分の直後に続きます。

以下の例では、Residenceクラスで定義されたサブスクリプトを使用して、john.residenceプロパティのrooms配列の最初の部屋の名前を取得しようとしています。john.residenceは現在でnilなので、サブスクリプト呼び出しは失敗します。

if let firstRoomName = john.residence?[0].name {
    print("The first room name is \(firstRoomName).")
} else {
    print("Unable to retrieve the first room name.")
}
// Prints "Unable to retrieve the first room name."

このサブスクリプト呼び出しのオプショナル連鎖疑問符は、john.residenceの直後、サブスクリプト括弧の前に配置されます。これは、john.residenceがオプショナル連鎖が試行されるオプショナル値であるためです。

同様に、オプショナル連鎖を伴うサブスクリプトを使用して、新しい値を設定することができます。

john.residence?[0] = Room(name: "Bathroom")

現在residenceは、nilであるため、このサブスクリプト設定の試行も失敗します。

実際のResidenceインスタンスを作成してjohn.residenceに割り当て、そのrooms配列に1つ以上のRoomインスタンスがある場合、Residenceサブスクリプトを使用して、オプショナルの連鎖を通じてrooms配列内の実際のアイテムにアクセスできます。

let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "Living Room"))
johnsHouse.rooms.append(Room(name: "Kitchen"))
john.residence = johnsHouse

if let firstRoomName = john.residence?[0].name {
    print("The first room name is \(firstRoomName).")
} else {
    print("Unable to retrieve the first room name.")
}
// Prints "The first room name is Living Room."

オプショナルタイプのサブスクリプトへのアクセス

サブスクリプトがオプショナルタイプの値(SwiftのDictionaryタイプのキーサブスクリプトなど)を返す場合は、サブスクリプトの閉じ括弧の後に疑問符を付けて、オプショナル戻り値を連鎖します。

var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0] += 1
testScores["Brian"]?[0] = 72
// the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81]

上の例では、testScoresという辞書を定義しています。これには、StringキーをInt値の配列にマップする2つのキーと値のペアが含まれています。この例では、オプショナル連鎖を使用して、"Dave"配列の最初の項目を91に設定しています。"Bev"配列の最初の項目を1;増分します。そして、配列の最初の項目を”Brian”のキーに設定しようとします。ので、最初の2つの呼び出しが、testScores辞書が"Dave""Bev"のキーが含まれているため、成功する。testScores辞書には”Brian”のキーが含まれていないため、3番目の呼び出しは失敗します。

複数レベルの連鎖のリンク

オプショナル連鎖の複数のレベルをリンクして、モデル内のより深いプロパティ、メソッド、およびサブスクリプトにドリルダウンできます。ただし、オプショナル連鎖の複数のレベルは、戻り値にオプショナルレベルを追加しません。

別の言い方をすると:

  • 取得しようとしている型がオプショナルではない場合、オプショナル連鎖のためにオプショナルになります。
  • あなたが取得しようとしている型がすでにオプショナルの場合はそれは連鎖のため、オプショナル以上にはなりません。

したがって:

  • オプショナル連鎖を通じてInt値を取得しようとすると、使用されている連鎖のレベルの数に関係なく、常にInt?が返されます。
  • 同様に、オプショナルの連鎖を通じてInt?値を取得しようとすると、使用される連鎖のレベルの数に関係なく、常にInt?が返されます。

以下の例では、johnのresidenceのプロパティのaddressのプロパティのstreetのプロパティをアクセスします。ここでは、どちらもオプショナル型のresidenceおよびaddressのプロパティを連鎖する、オプショナル連鎖の2つのレベルがあります:

if let johnsStreet = john.residence?.address?.street {
    print("John's street name is \(johnsStreet).")
} else {
    print("Unable to retrieve the address.")
}
// Prints "Unable to retrieve the address."

john.residenceの値には現在、有効なResidenceインスタンスが含まれています。ただし、john.residence.address の値は現在nilです。このため、john.residence?.address?.street への呼び出しは失敗します。

上記の例では、streetプロパティの値を取得しようとしていることに注意してください。このプロパティの型はString?です。したがって、プロパティの基になるオプショナルタイプに加えて、2つのレベルのオプショナルの連鎖が適用されていても、john.residence?.address?.streetの戻り値はString?です。

実際のAddressインスタンスをjohn.residence.addressの値として設定し、アドレスのstreetプロパティに実際の値を設定すると、マルチレベルのオプショナルの連鎖を通じてstreetプロパティの値にアクセスできます。

let johnsAddress = Address()
johnsAddress.buildingName = "The Larches"
johnsAddress.street = "Laurel Street"
john.residence?.address = johnsAddress

if let johnsStreet = john.residence?.address?.street {
    print("John's street name is \(johnsStreet).")
} else {
    print("Unable to retrieve the address.")
}
// Prints "John's street name is Laurel Street."

この例では、john.residenceのaddressプロパティを設定する試みは成功します。これは、john.residenceの値に現在有効なResidenceインスタンスが含まれているためです。

オプショナル戻り値を持つメソッドの連鎖

前の例は、オプショナル連鎖を通じてオプショナルプロパティの値を取得する方法を示しています。オプショナル連鎖を使用して、オプショナル型の値を返すメソッドを呼び出し、必要に応じてそのメソッドの戻り値を連鎖することもできます。

以下の例では、オプショナル連鎖を通じてAddressクラスのbuildingIdentifier()メソッドを呼び出しています。このメソッドは、String?型の値を返します。上記のように、オプショナルの連鎖後のこのメソッド呼び出しの最終的な戻り値の型も次のとおりString?です。

if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
    print("John's building identifier is \(buildingIdentifier).")
}
// Prints "John's building identifier is The Larches."

このメソッドの戻り値に対してさらにオプショナルの連鎖を実行する場合は、メソッドの括弧の後にオプショナルの連鎖の疑問符を配置します。

if let beginsWithThe =
    john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
    if beginsWithThe {
        print("John's building identifier begins with \"The\".")
    } else {
        print("John's building identifier does not begin with \"The\".")
    }
}
// Prints "John's building identifier begins with "The"."

注意

上記の例では、オプショナル連鎖の疑問符を括弧の後に配置しています。これは、連鎖するオプショナル値がbuildingIdentifier()メソッドの戻り値であり、buildingIdentifier()メソッド自体ではないためです。

クラスの終了処理(deinit)

クラスの終了処理 deinitの定義

deinit {
    // パラメータ無し(中括弧無し)
    // クラスのみに使用できる
}

デイニシャライザは、クラスインスタンスの割り当てが解除される直前に呼び出されます。デイニシャライザは、クラスタイプでのみ使用できます。

デイニシャライザの例

        let dog = Dog(name: "Snoopy")
        print(dog.name)
// 結果
// Snoopy
// 当該クラスの deinit が呼ばれる
// deinit Dog   
// 次にスーパークラスの deinit が呼ばれる
// deinit Animal
    
    class Animal {
        var name: String
        var nakigoe: String?
        init(name: String) {
            self.name = name
            self.nakigoe = "designated wanwan"
        }
        deinit {
            print("deinit Animal")
        }
    }

    class Dog: Animal {
        deinit {
            print("deinit Dog")
        }
    }
    

デイニシャライズ

デイニシャライザは、クラスインスタンスの割り当てが解除される直前に呼び出されます。deinitキーワードを使用してイニシャライザinitを作成する方法と同様に、キーワードを使用してデイニシャライザを作成します。デイニシャライザは、クラスタイプでのみ使用できます。

デイニシャライズのしくみ

Swiftは、インスタンスが不要になったときに自動的に割り当てを解除して、リソースを解放します。Swiftは自動参照カウントを通じてインスタンスのメモリ管理を処理する自動参照カウント。通常、インスタンスの割り当てが解除されたときに手動でクリーンアップを実行する必要はありません。ただし、独自のリソースを使用している場合は、追加のクリーンアップを自分で実行する必要がある場合があります。たとえば、ファイルを開いてデータを書き込むカスタムクラスを作成する場合、クラスインスタンスの割り当てを解除する前にファイルを閉じる必要がある場合があります。

クラス定義は、クラスごとに最大1つの初期化子を持つことができます。デイニシャライザはパラメータを取らず、括弧なしで記述されます。

deinit {
// perform the deinitialization
}

デイニシャライザは、インスタンスの割り当て解除が行われる直前に自動的に呼び出されます。自分でデイニシャライザを呼び出すことはできません。スーパークラスのデイニシャライザはサブクラスによって継承され、スーパークラスのデイニシャライザはサブクラスのデイニシャライザの実装の最後に自動的に呼び出されます。サブクラスが独自のデイニシャライザを提供しない場合でも、スーパークラスのデイニシャライザは常に呼び出されます。

インスタンスはデイニシャライザが呼び出されるまで割り当て解除されないため、デイニシャライザは呼び出されたインスタンスのすべてのプロパティにアクセスでき、それらのプロパティに基づいて動作を変更できます(閉じる必要のあるファイルの名前の検索など) )。

実行中のデイニシャライザ

動作中のデイニシャライザの例を次に示します。この例では、単純なゲーム用に2つの新しいタイプ、BankとPlayerを定義しています。このBankクラスは、1万枚を超えるコインを流通させることのできない構成通貨を管理します。ゲームにはBankは1つしか存在できないため、Bankは、現在の状態を格納および管理するためのタイププロパティとメソッドを備えたクラスとして実装されます。

class Bank {
    static var coinsInBank = 10_000
    static func distribute(coins numberOfCoinsRequested: Int) -> Int {
        let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank)
        coinsInBank -= numberOfCoinsToVend
        return numberOfCoinsToVend
    }
    static func receive(coins: Int) {
        coinsInBank += coins
    }
}

Bankは、そのcoinsInBankプロパティで保持しているコインの現在の数を追跡します。また、コインの配布・回収を扱う 2つのメソッド distribute(coins:)とreceive(coins:)を提供します。

このdistribute(coins:)メソッドは、銀行に十分な量のコインがあることを確認してから、配布します。十分なコインがない場合、Bankは要求された数よりも小さい数を返します(銀行にコインが残っていない場合はゼロを返します)。提供されたコインの実際の数を示す整数値を返します。

このreceive(coins:)メソッドは、受け取った数のコインをBankのコインストアに追加します。

このPlayerクラスは、ゲーム内でプレイヤーを説明しています。各プレーヤーは、いつでも一定数のコインを財布に保管しています。これは、プレーヤーのcoinsInPurseプロパティによって表されます。

class Player {
    var coinsInPurse: Int
    init(coins: Int) {
        coinsInPurse = Bank.distribute(coins: coins)
    }
    func win(coins: Int) {
        coinsInPurse += Bank.distribute(coins: coins)
    }
    deinit {
        Bank.receive(coins: coinsInPurse)
    }
}

Playerインスタンスは、初期化中に銀行から指定された数のコインで初期化されますが、Playerが十分なコインが利用できない場合、インスタンスはその数よりも少ない数を受け取ることがあります。

Playerクラスがwin(coins:)メソッドを定義し、Bankからコインの特定の数を取得し、遊技者の財布にそれらを追加します。Playerクラスには、Playerのインスタンスが解放される直前に呼び出されるデイニシャライザを実装します。ここで、デイニシャライザは単純にすべてのプレーヤーのコインを銀行に返します。

var playerOne: Player? = Player(coins: 100)
print("A new player has joined the game with \(playerOne!.coinsInPurse) coins")
// Prints "A new player has joined the game with 100 coins"
print("There are now \(Bank.coinsInBank) coins left in the bank")
// Prints "There are now 9900 coins left in the bank"

新しいPlayerインスタンスが作成され、利用可能な場合は100コインがリクエストされます。このPlayerインスタンスは、playerOneというオプションの変数に格納されます。プレーヤーはいつでもゲームを離れることができるため、ここではオプションの変数が使用されています。オプションを使用すると、現在ゲームにプレーヤーがいるかどうかを追跡できます。

playerOneはオプションであるため、そのcoinsInPurseプロパティにアクセスしてデフォルトのコイン数を出力するとき、およびそのwin(coins:)メソッドが呼び出されるときは常に、感嘆符(!)で修飾されます。

playerOne = nil
print("PlayerOne has left the game")
// Prints "PlayerOne has left the game"
print("The bank now has \(Bank.coinsInBank) coins")
// Prints "The bank now has 10000 coins"
  1. playerOne!.win(coins: 2_000)
  2. print(“PlayerOne won 2000 coins & now has \(playerOne!.coinsInPurse) coins”)
  3. // Prints “PlayerOne won 2000 coins & now has 2100 coins”
  4. print(“The bank now only has \(Bank.coinsInBank) coins left”)
  5. // Prints “The bank now only has 7900 coins left”

ここでは、プレイヤーは2,000コインを獲得しています。プレイヤーの財布には2,100コインが含まれ、銀行には7,900コインしか残っていません。

  1. playerOne = nil
  2. print(“PlayerOne has left the game”)
  3. // Prints “PlayerOne has left the game”
  4. print(“The bank now has \(Bank.coinsInBank) coins”)
  5. // Prints “The bank now has 10000 coins”

プレイヤーはゲームを離れました。これは、オプションのplayerOne変数を「Playerインスタンスなし」を意味するnilに設定することで示されます。これが発生した時点で、playerOne変数へのPlayerインスタンスへの参照は壊れています。他のプロパティや変数はまだPlayerインスタンスを参照していないため、メモリを解放するために割り当てが解除されます。これが発生する直前に、そのデイニシャライザが自動的に呼び出され、そのコインが銀行に返されます。

初期化2

Swift programming language の Initialization の章が分かりにくいので、初期化2に簡単にまとめます。

イニシャライザー

// 最も簡単なイニシャライザー
    class Animal {
        var name: String = ""
        var nakigoe: String = ""
        init() {
        }
    }

// init でプロパティ値を初期化
    class Animal {
        var name: String
        var nakigoe: String
        init() {
            name = ""
            nakigoe = ""
        }
    }

// initの書式
//  init(ラベル名 引数名: 型) { 式 }
  let animal = Animal(animalName: "animal")

    class Animal {
        var name: String
        var nakigoe: String?
        init(animalName name: String) {
            self.name = name
        }
    }

// 引数ラベルのない初期化パラメーター
  let animal = Animal("animal")

    class Animal {
        var name: String
        var nakigoe: String?
        init(_ name: String) {
            self.name = name
        }
    }

// すべてのプロパティのデフォルト値が提供されている場合、initは省略できる
// nakigoeは、オプショナルString のため、デフォルト値 nil が与えられる
    class Animal {
        var name: String = ""
        var nakigoe: String?
    }
    
// メンバーの初期化例
  let animal = Animal(name: "animal", nakigoe: nil)

    class Animal {
        var name: String = ""
        var nakigoe: String? = nil
        init(name: String, nakigoe: String?) {
            self.name = name
            self.nakigoe = nakigoe
        }
    }

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

// 指定イニシャライザ
// すべてのクラスには、少なくとも1つの指定イニシャライザが必要
init(parameters) {
    statements
}

// コンビニエンスイニシャライザは二次的なもの
// クラスのイニシャライザをサポートする
convenience init(parameters) {
    statements
}
  • ルール1 指定イニシャライザは、直接のスーパークラスから指定イニシャライザを呼び出す必要があります。
  • ルール2 コンビニエンスイニシャライザは、同じクラスから別のイニシャライザを呼び出す必要があります。
  • ルール3 コンビニエンスイニシャライザは、最終的に指定イニシャライザを呼び出す必要があります。
../_images/initializerDelegation01_2x.png
    class Animal {
        var name: String
        var nakigoe: String?
        // 指定イニシャライザー
        init(_ name: String) {
            self.name = name
        }
        // コンビニエンスイニシャライザー
        convenience init(name: String, nakigoe: String)
        {
            self.init(name)
            self.nakigoe = nakigoe
        }
        func naku(){
            print(nakigoe ?? "")
        }
    }
// スーパークラス内のコンビニエンスイニシャライザー
        convenience init(name: String, nakigoe: String)

// サブクラスからスーパークラスのコンビニエンスイニシャライザーを
// オーバーライドしようとすると、次のエラーが発生する
        override convenience init(name: String, nakigoe: String)
// Initializer does not override a designated initializer from its superclass

2フェーズ初期化

フェーズ1では、すべての格納プロパティに初期値が割り当てられます。
フェーズ2では、各クラスに格納されたプロパティをカスタマイズできる。

    class Animal {
        var name: String
        var nakigoe: String?
        init(_ name: String) {
            self.name = name
        }
        convenience init(name: String, nakigoe: String)
        {
            self.init(name)
            self.nakigoe = nakigoe
        }
        func naku(){
            print(nakigoe ?? "")
        }
    }

    class Dog: Animal {
        override init(_ name: String) {
            // フェーズ1  スーパークラスのイニシャライザーを呼ぶ必要がある
            super.init(name)
            // フェーズ2 カスタマイズできる
            self.nakigoe = "wanwan" 
        }
    }

// super.init を呼ばないと、次のエラーになる
// 'self' used in property access 'nakigoe' before 'super.init' call
// 'super.init' isn't called on all paths before returning from initializer

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

// ルール1
// サブクラスが指定イニシャライザを定義していない場合、
// 自動的に Animal のinit が継承される
   class Animal {
        var name: String
        var nakigoe: String?
        init(_ name: String) {
            self.name = name
            self.nakigoe = "designated wanwan"
        }
        convenience init(name: String, nakigoe: String)
        {
            self.init(name)
            self.nakigoe = "convinience wanwa"
        }
        func naku(){
            print(nakigoe ?? "")
        }
    }

    class Dog: Animal {
        // サブクラスが指定イニシャライザを定義していない場合、
        // 自動的に Animal のinit が継承される
    }

//         let dog = Dog("Snoopy")
//        dog.naku()
// 結果: designated wanwan
// ルール2
// サブクラスがすべてのスーパークラスの指定イニシャライザの実装を提供する場合
// スーパークラスのすべてのコンビニエンスイニシャライザを自動的に継承する
    class Animal {
        var name: String
        var nakigoe: String?
        init(_ name: String) {
            self.name = name
            self.nakigoe = "designated wanwan"
        }
        convenience init(name: String, nakigoe: String)
        {
            self.init(name)
            self.nakigoe = nakigoe
        }
        func naku(){
            print(nakigoe ?? "")
        }
    }

    class Dog: Animal {
        override init(_ name: String) {
            super.init(name)
            self.name = name
            self.nakigoe = "designated wanwan"
        }
    }

//        let dog = Dog(name: "Snoopy", nakigoe: "superclass's convinience init wanwan")
//       dog.naku()
// 結果: superclass's convinience init wanwan

失敗可能なイニシャライザ

失敗可能なイニシャライザは、初期化する型のオプションの値を作成します。失敗可能なイニシャライザ内に return nil を書き込んで、初期化の失敗をトリガーします。

        if let animal = Animal("")
        {
            // 初期化に失敗して、animal はnil になるため、次の行は実行されない
            animal.naku()
        }
    
    class Animal {
        var name: String
        var nakigoe: String?
        // 失敗可能なイニシャライザ
        init?(_ name: String?) {
            if name == "" {
                // return nil で初期化に失敗したことをトリガーする
                return nil
            }
            self.name = name!
            self.nakigoe = "designated wanwan"
        }
        func naku(){
            print(nakigoe ?? "")
        }
    }

クラス、構造体、または列挙体の失敗可能なイニシャライザは、同じクラス、構造体、または列挙体から別の失敗可能なイニシャライザに委任できます。

サブクラスでスーパークラスの失敗可能なイニシャライザをオーバーライドできます。また、スーパークラスの初期化に失敗しても、初期化に失敗しないサブクラスでオーバーライドできます。

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

暗黙的にアンラップされたオプションのインスタンスを作成する、失敗可能なイニシャライザ init! を定義できます。

        if let animal = Animal("")
        {
            animal.naku()
        }
    
    class Animal {
        var name: String
        var nakigoe: String?
        init!(_ name: String?) {
            if name == "" {
                self.name = "default name"
            }
            self.name = name!
            self.nakigoe = "designated wanwan"
        }
        func naku(){
            print(nakigoe ?? "")
        }
    }

必須イニシャライザ

    class Animal {
        var name: String
        var nakigoe: String?
        // すべてのサブクラスがそのイニシャライザを実装する必要があることを示す
        required init(name: String) {
            self.name = name
            self.nakigoe = "wanwan"
        }
    }

    class Dog: Animal {
        // override は不要
        required init(name: String)
        {
            super.init(name: name)
            self.name = "dog: " + name
        }