Swiftのプロパティ

7612 ワード

1.記憶属性


簡単に言えば、各ストレージ属性は、特定のクラスまたは構造体インスタンスに格納される定数または変数です.ストレージ・プロパティは、変数にプロパティを格納するか、定数ストレージ・プロパティを格納します.

1.1定数構造体の記憶属性


構造体は値タイプに属し、値タイプのインスタンスが定数として宣言されると、すべてのプロパティが定数になります.
参照タイプのクラスは異なり、参照タイプのインスタンスを定数に割り当てると、そのインスタンスの変数属性を変更できます.

1.2遅延ストレージ属性


遅延ストレージ属性とは、最初に呼び出されたときにその初期値が計算される属性です.属性宣言の前にlazyを使用して、遅延ストレージ属性を表示します.
注意:インスタンス構築が完了してから、属性の初期値が得られる可能性があるため、遅延ストレージ属性は変数として宣言する必要があります(varキーワードを使用します).定数属性は、構築プロセスが完了する前に初期値が必要であるため、遅延属性として宣言できません.
遅延プロパティは、インスタンスの構築プロセスが終了してから値に影響する外部要因がわかるようにプロパティの値に依存する場合や、プロパティの初期値を取得するのに複雑または大量の計算が必要な場合にのみ計算できます.
class DataImporter {
    var fileName = "data.txt"
}

class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
}

let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// DataImporter   importer  
lazyimporter属性が使用されているため、最初にアクセスされたときにのみ作成されます.たとえば、プロパティfileNameにアクセスした場合:
print(manager.importer.fileName)

注意:lazyとマークされた属性が初期化されずに複数のスレッドに同時にアクセスされた場合、その属性が一度だけ初期化されることは保証されません.

1.3属性とインスタンス変数の格納


Objective-Cの経験がある場合は、Objective-Cがクラスインスタンスに値と参照を格納する2つの方法を提供することを知っておく必要があります.プロパティに加えて、インスタンス変数をプロパティ値のバックエンドとして格納することもできます.
Swiftプログラミング言語ではこれらの理論を属性で統一して実現する.Swiftの属性には対応するインスタンス変数がなく,属性のバックエンドストレージにも直接アクセスできない.これにより、異なるシーンでのアクセス方法の問題を回避し、プロパティの定義を文に簡略化できます.アトリビュートのすべての情報(名前、タイプ、メモリ管理フィーチャーを含む)は、ユニークな場所(タイプ定義)で定義されます.

2.計算属性


クラス、構造体、列挙は、ストレージ属性に加えて計算属性を定義できます.計算属性は、値を直接格納するのではなく、getterとオプションのsetterを提供し、他の属性または変数の値を間接的に取得および設定します.
struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set(newCenter) {
            origin.x = newCenter.x - (size.width / 2)
            origin.y = newCenter.y - (size.height / 2)
        }
    }
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
                  size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
//   "square.origin is now at (10.0, 10.0)”

2.1 setter宣言の簡略化


計算属性のsetterに新しい値を表すパラメータ名が定義されていない場合は、デフォルト名newValueを使用します.次に、setter宣言を簡略化したRect構造体コードを示す.
struct AlternativeRect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}

2.2読取り専用計算属性

getterのみsetterの計算属性がなく、読み取り専用計算属性である.読み取り専用計算プロパティは、ポイント演算子でアクセスできる値を常に返しますが、新しい値を設定することはできません.
注意:varキーを使用して、値が固定されていないため、読み取り専用の計算プロパティを含む計算プロパティを定義する必要があります.letキーワードは定数属性を宣言するためにのみ使用され、初期化後に変更できない値を表します.
読み取り専用計算プロパティの宣言により、getのキーワードとカッコを削除できます.
struct Cuboid {
    var width = 0.0, height = 0.0, depth = 0.0
    var volume: Double {
        return width * height * depth
    }
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
//   "the volume of fourByFiveByTwo is 40.0"

3.属性オブザーバ


アトリビュートオブザーバは、アトリビュート値の変化を監視し、応答します.アトリビュート値が設定されるたびにアトリビュートオブザーバが呼び出されます.新しい値と現在の値が同じ場合でも例外ではありません.
  • willSet新しい値が設定前に
  • が呼び出される.
  • didSetは、新しい値が設定された直後に
  • を呼び出す.
    注:親のプロパティが子のコンストラクタに割り当てられている場合、親のwillSetおよびdidSetのオブジェクトが呼び出され、その後、子のオブジェクトが呼び出されます.親初期化メソッドが呼び出される前に、サブクラスがプロパティに値を割り当てると、オブザーバは呼び出されません.
    class StepCounter {
        var totalSteps: Int = 0 {
            willSet(newTotalSteps) {
                print("About to set totalSteps to \(newTotalSteps)")
            }
            didSet {
                if totalSteps > oldValue {
                    print("Addedd \(totalSteps - oldValue) steps")
                }
            }
        }
    }
    
    let stepCounter = StepCounter()
    stepCounter.totalSteps = 200
    // About to set totalSteps to 200
    // Added 200 steps
    stepCounter.totalSteps = 360
    // About to set totalSteps to 360
    // Added 160 steps
    stepCounter.totalSteps = 896
    // About to set totalSteps to 896
    // Added 536 steps
    

    注:属性がin-outで関数に入力されると、willSetおよびdidSetも呼び出されます.これは、in-outパラメータがコピーコピーモードを採用しているためである.すなわち、関数内部でパラメータのcopyが使用され、関数が終了した後、パラメータに再付与される.

    4.グローバル変数とローカル変数


    グローバルの定数または変数はいずれも遅延計算であり、異なる点は、グローバルの定数または変数はlazy修飾子をマークする必要がないことである.
    ローカル範囲の定数または変数は、計算を遅延しません.

    5.タイプ属性


    インスタンス属性は特定のタイプのインスタンスに属し、インスタンスを作成するたびに、インスタンスは独自の属性値を持ち、インスタンス間の属性は互いに独立しています.
    また、タイプ自体のプロパティを定義することもできます.このタイプのインスタンスがいくつ作成されても、これらのプロパティは唯一です.この属性がタイプ属性です.
    タイプ属性は、すべてのインスタンスが使用できる定数(C言語の静的定数のような)や、すべてのインスタンスがアクセスできる変数(C言語の静的変数のような)など、すべてのインスタンスが共有するデータを定義するために使用されます.
    ストレージ・タイプ・プロパティは変数または定数であり、計算タイプ・プロパティはインスタンスの計算タイプ・プロパティと同様に変数プロパティとして定義できます.
    注:インスタンスのストレージ・プロパティとは異なり、タイプ自体にコンストラクタがないため、初期化中にコンストラクタを使用してタイプ・プロパティに値を割り当てることはできません.ストレージ・タイプ・プロパティは、最初にアクセスしたときにのみ初期化される遅延初期化です.複数のスレッドに同時にアクセスされても、システムは、lazy修飾子を使用する必要がなく、一度だけ初期化されることを保証します.

    5.1型属性構文


    キーワードstaticを使用してタイプ属性を定義します.クラスに計算型タイプ属性を定義する場合、キーワードclassを変更して、親クラスの実装をサブクラスで書き換えることをサポートすることができる.次の例では、ストレージタイプと計算タイプのプロパティの構文を示します.
    struct SomeStructure {
        static var storedTypeProperty = "Some Value."
        static var computedTypeProperty: Int {
            return 1
        }
    }
    
    enum SomeEnumeration {
        static var storedTypeProperty = "Some Value."
        static var computedTypeProperty: Int {
            return 6
        }
    }
    
    class SomeClass {
        static var storedTypeProperty = "Some Value."
        static var computedTypeProperty: Int {
            return 27
        }
        class var overrideableComputedTypeProperty: Int {
            return 107
        }
    }
    

    5.2タイプ属性の値の取得と設定


    インスタンス属性と同様に、タイプ属性もポイント演算子でアクセスします.ただし、タイププロパティは、インスタンスではなくタイプ自体でアクセスします.
    struct AudioChannel {
        static let thresholdLEvel = 10
        static var maxInputLevelForAllChannels = 0
        var currentLevel: Int = 0 {
            didSet {
                if currentLevel > AudioChannel.thresholdLEvel {
                    currentLevel = AudioChannel.thresholdLEvel
                }
                if currentLevel > AudioChannel.maxInputLevelForAllChannels {
                    AudioChannel.maxInputLevelForAllChannels = currentLevel
                }
            }
        }
    }
    
    var leftChannel = AudioChannel()
    var rightChannel = AudioChannel()
    
    leftChannel.currentLevel = 7
    print(leftChannel.currentLevel)
    // print "7"
    
    print(AudioChannel.maxInputLevelForAllChannels)
    // print "7"
    
    rightChannel.currentLevel = 11
    print(rightChannel.currentLevel)
    // print "10"
    print(AudioChannel.maxInputLevelForAllChannels)
    // print "10"