swift学習ノート(10)---属性

8812 ワード

プロパティは、値を特定のクラス、構造体、または列挙に関連付けます.属性は次のように分けられます.
  • ストレージ属性:定数と変数をインスタンスの一部として保存し、クラスと構造体にのみ使用できます.
  • プロパティの計算:クラス、構造体、列挙に使用できる直接計算値.

  • プロパティは、タイプ自体に直接関連付けることもできます.このプロパティをタイププロパティと呼びます.
    また、アトリビュートオブザーバを定義してアトリビュート値の変化を監視し、カスタム操作をトリガーすることもできます.プロパティ・オブザーバは、クラス自体が定義したストレージ・プロパティに追加したり、親から継承したプロパティに追加したりできます.

    1、記憶属性


    ストレージ属性は、特定のクラスまたは構造体インスタンスに格納される定数または変数です.ストレージ属性を定義するときにデフォルト値を指定するか、構築中にストレージ属性の値を設定または変更したり、定数ストレージ属性の値を変更したりすることができます.
    struct FixedLengthRange {
        var firstValue: Int
        let length: Int
    }
    var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
    //   0,1,2
    rangeOfThreeItems.firstValue = 6
    //   6,7,8
    

    (1)定数構造体インスタンスの記憶属性
    構造体インスタンスを作成して定数に値を割り当てると、インスタンスのプロパティは変更できません.可変プロパティとして宣言されても変更できません.
    let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
    //   0,1,2,3
    rangeOfFourItems.firstValue = 6
    //   firstValue  , 
    

    この動作は、構造体が値タイプに属し、値タイプのインスタンスが定数として宣言されると、そのすべての属性が定数になるためです.
    (2)遅延ロード記憶属性
    遅延ロード記憶属性とは、最初に呼び出されたときにその初期値が計算される属性であり、属性宣言前にlazyを使用して遅延ロード記憶属性を示す.
    class DataImporter {
        /*
        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  
    print(manager.importer.fileName)
    // DataImporter   importer  
    //  “data.txt”
    

    注意:
  • は、インスタンス構築が完了した後に属性の初期値が得られる可能性があるため、遅延ロード属性を変数として宣言する必要があります.定数プロパティは、コンストラクションプロセスが完了する前に初期値が必要であるため、遅延ロードとして宣言できません.
  • lazyとマークされた属性が初期化されていないときに同時に複数のスレッドにアクセスされる場合、その属性が一度だけ初期化されることは保証されません.

  • (3)属性とインスタンス変数の格納
    Objective-Cは、クラスインスタンスに値を格納する方法と、属性に加えて、インスタンス変数をバックアップストレージとして使用して変数値を属性に割り当てる方法の2つを提供します.Swiftはこれらの理論を属性で統一して実現した.Swiftの属性には対応するインスタンス変数がなく、属性のバックアップストレージにも直接アクセスできません.これにより、異なるシーンでのアクセス方法の問題を回避し、プロパティの定義を文に簡略化できます.属性のすべての情報(名前、タイプ、メモリ管理フィーチャーを含む)は、タイプ定義の一部として1つの場所に定義されます.

    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)”
    

    (1)簡略化setter声明
    計算属性のsetterに新しい値を表すパラメータ名が定義されていない場合は、デフォルト値名newValueを使用します.
    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)簡略化getter声明getter全体が単一の式である場合、getterはこの式の結果を暗黙的に返します.
    struct CompactRect {
        var origin = Point()
        var size = Size()
        var center: Point {
            get {
                Point(x: origin.x + (size.width / 2),
                      y: origin.y + (size.height / 2))
            }
            set {
                origin.x = newValue.x - (size.width / 2)
                origin.y = newValue.y - (size.height / 2)
            }
        }
    }
    

    (3)読み取り専用計算属性getterのみsetterの計算属性を読み取り専用計算属性と呼ぶ.読み取り専用計算プロパティは、ポイント演算子でアクセスできる値を常に返しますが、新しい値を設定することはできません.
    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”
    

    注意:
  • 読取り専用計算属性の宣言は、getキーワードとカッコ
  • を削除することができる.
  • は、値が固定されていないため、varキーワードを使用して計算属性を定義する必要があります.letキーワードは定数属性を宣言するためにのみ使用され、初期化後二度と変更できない値
  • を表す.

    3、属性オブザーバ


    アトリビュートオブザーバは、アトリビュート値の変化を監視し、応答します.アトリビュート値が設定されるたびにアトリビュートオブザーバが呼び出されます.新しい値と現在の値が同じ場合でも例外ではありません.
    アトリビュートにオブジェクトを追加するには:
  • willSetは新しい値が設定される前に呼び出され、デフォルト名newValue
  • を表す.
  • didSet新しい値が設定された後に呼び出す、デフォルトパラメータ名oldValue
  • .
    class StepCounter {
        var totalSteps: Int = 0 {
            willSet(newTotalSteps) {
                print("  totalSteps   \(newTotalSteps)")
            }
            didSet {
                if totalSteps > oldValue  {
                    print("  \(totalSteps - oldValue)  ")
                }
            }
        }
    }
    let stepCounter = StepCounter()
    stepCounter.totalSteps = 200
    //   totalSteps   200
    //   200  
    stepCounter.totalSteps = 360
    //   totalSteps   360
    //   160  
    stepCounter.totalSteps = 896
    //   totalSteps   896
    //   536  
    

    注意:
  • 親初期化メソッド呼び出しの後、子コンストラクタで親のプロパティに値を割り当てると、親のプロパティのwillSetおよびdidSetオブザーバが呼び出されます.親初期化メソッドが呼び出される前に、子クラスのプロパティに値を割り当てると、子クラスのプロパティのオブザーバは呼び出されません.
  • が、in-outによってオブザーバ付き属性を伝達する場合、willSetおよびdidSetも呼び出される.これは、in-outパラメータがコピーメモリモードを採用しているためである.すなわち、関数内部でパラメータのcopyが使用され、関数が終了した後、パラメータに値を再割り当てする.

  • 4、グローバル変数とローカル変数

  • 属性および観察属性を計算するために記述された機能は、グローバル変数およびローカル変数
  • にも使用することができる.
  • グローバルの定数または変数はいずれも遅延計算であり、遅延ロード記憶属性と同様であり、異なる点は、グローバルの定数または変数はlazy修飾子をマークする必要がないことである.
  • 局所範囲の定数と変数無遅延計算
  • 5、タイプ属性


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

  • (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
        }
    }
    

    (2)タイプ属性の値の取得と設定
    インスタンス属性と同様に、タイプ属性もポイント演算子でアクセスします.ただし、タイププロパティは、インスタンスではなくタイプ自体でアクセスします.
    print(SomeStructure.storedTypeProperty)
    //  “Some value.”
    SomeStructure.storedTypeProperty = "Another value."
    print(SomeStructure.storedTypeProperty)
    //  “Another value.”
    print(SomeEnumeration.computedTypeProperty)
    //  “6”
    print(SomeClass.computedTypeProperty)
    //  “27”
    

    次の例では、2つのストレージタイプ属性を使用して2つのチャネルの音量を表し、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)
    //  “7”
    print(AudioChannel.maxInputLevelForAllChannels)
    //  “7”
    rightChannel.currentLevel = 11
    print(rightChannel.currentLevel)
    //  “10”
    print(AudioChannel.maxInputLevelForAllChannels)
    //  “10”