Swift-プロパティ

7023 ワード

ストレージのプロパティ


最も単純な形式では、ストレージ属性は、特定のクラスまたは構造のインスタンスの一部として格納される定数または変数である.格納される属性は、変数格納属性(varキーワードによって導入される)または定数格納属性(キーワードによって導入される)であってもよい.
「≪記憶域プロパティ|Storage Properties|emdw≫」の「≪デフォルト|Default|emdw≫」の説明に従って、記憶域プロパティのデフォルト値を定義の一部として指定できます.また、初期化中にストレージ属性の初期値を設定および変更することもできます.定数ストレージの属性についても、初期化時に定数属性を割り当てるには、「」の説明に従ってください.
次の例では、作成後に範囲の長さを変更できない一連の整数を記述するFixedLengthRangeという構造を定義します.
struct FixedLengthRange {
    var firstValue: Int
    let length: Int
}
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// the range represents integer values 0, 1, and 2
rangeOfThreeItems.firstValue = 6
// the range now represents integer values 6, 7, and 8

FixedLengthRangeの例にはfirstValueと呼ばれる変数格納属性とlengthと呼ばれる定数格納属性がある.上記の例では、長さは新しい範囲を作成するときに初期化され、定数属性であるため、その後は変更できません.

定数構造インスタンスのストレージ属性


構造のインスタンスを作成して定数に割り当てると、変数属性として宣言されても、インスタンスの属性は変更できません.
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// this range represents integer values 0, 1, 2, and 3
rangeOfFourItems.firstValue = 6
// this will report an error, even though firstValue is a variable property

rangeOfFourItemsは定数(letキーを使用)として宣言されているため、firstValueが変数属性であってもfirstValue属性を変更することはできません.
この挙動は,構造が値タイプであるためである.値タイプのインスタンスが定数としてマークされている場合、そのすべてのプロパティも同様です.
参照タイプであるクラスではそうではありません.参照タイプのインスタンスを定数に割り当てる場合は、インスタンスの変数プロパティを変更できます.

遅延ストレージ属性


遅延ストレージ属性は、最初の使用時に初期値が計算されない属性です.宣言する前にlazy修飾子を書き込むことで、不活性なストレージ属性を示します.
注意:lazyプロパティは、インスタンスの初期化が完了した後に取得できない可能性があるため、常に変数として宣言する必要があります(varキーワードを使用します).定数属性は、初期化が完了するまで常に値を持つ必要があるため、遅延として宣言できません.
次の例では、不要な複雑なクラスの初期化を回避するために、遅延ストレージ属性を使用します.この例では、DataImporterとDataManagerという2つのクラスを定義します.どちらのクラスも完全に表示されません.
class DataImporter {
    /*
     DataImporter is a class to import data from an external file.
     The class is assumed to take a non-trivial amount of time to initialize.
     */
    var fileName = "data.txt"
    // the DataImporter class would provide data importing functionality here
}

class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
    // the DataManager class would provide data management functionality here
}

let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// the DataImporter instance for the importer property has not yet been created

lazy修飾子でタグ付けされているため、fileNameプロパティのクエリーなど、importerプロパティに初めてアクセスしたときにのみ、importerプロパティのDataImporterインスタンスが作成されます.
print(manager.importer.fileName)
// the DataImporter instance for the importer property has now been created
// Prints "data.txt"

lazy修飾子がマークされたプロパティが複数のスレッドで同時にアクセスされ、まだ初期化されていない場合は、プロパティが一度だけ初期化されることは保証されません.

属性とインスタンス変数の保存


Objective-Cの経験がある場合は、クラスインスタンスの一部として値を格納し、参照する2つの方法を提供していることがわかります.属性に加えて、インスタンス変数を属性に格納された値のバックアップとして使用できます.
Swiftはこれらの概念を単一の属性宣言に統一した.Swift属性には対応するインスタンス変数がなく、属性のバックアップストレージに直接アクセスしません.この方法では、異なるコンテキストで値にどのようにアクセスするかの混同を回避し、属性の宣言を単一の決定文に簡略化します.属性に関するすべての情報(名前、タイプ、メモリ管理プロパティを含む)は、タイプ定義の一部として単一の場所で定義されます.

計算プロパティ


格納されたプロパティ、クラス、構造、列挙を除いて、計算プロパティを定義できますが、実際には値は格納されません.逆に、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))")
// Prints "square.origin is now at (10.0, 10.0)"

速記組合宣言


計算プロパティのsetterで設定する新しい値の名前が定義されていない場合は、デフォルトの名前newValueを使用します.ここには、この略記記号を利用した代替バージョンの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)
        }
    }
}

読み取り専用計算プロパティ


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)")
// Prints "the volume of fourByFiveByTwo is 40.0"

属性オブザーバ


プロパティオブザーバは、プロパティ値の変化を観察し、応答します.アトリビュートの値を設定するたびに、新しい値がアトリビュートの現在の値と同じであっても、アトリビュートオブザーバが呼び出されます.
プロパティ・オブザーバは、lazyストレージ・プロパティを除く定義した任意のストレージ・プロパティに追加できます.また、サブクラスのアトリビュートを上書きして、アトリビュートオブザーバを継承されたアトリビュート(ストレージまたは計算にかかわらず)に追加することもできます.上書きされていない計算プロパティのプロパティオブザーバを定義する必要はありません.計算プロパティのsetterで値の変更を観察し、鳴らすことができます.アトリビュートオーバーライド(Attribute Override)はオーバーライド(Override)で説明されています.
この2つのオブザーバーの1つをアトリビュートで定義するには、次のいずれかを選択します.
  • willSetは、値が格納される前に呼び出される.
  • didSetは、新しい値が格納された直後に呼び出される.

  • willSetオブザーバーを実装すると、新しいプロパティ値が定数パラメータとして渡されます.このパラメータの名前はwillSet実装の一部として指定できます.インプリメンテーションにパラメータ名とカッコが書き込まれていない場合、そのパラメータのデフォルトのパラメータ名はnewValueです.
    同様に、didSetオブザーバーを実装すると、定数パラメータに古い属性値が含まれていることが伝わります.パラメータの名前を付けるか、デフォルトのパラメータ名oldValueを使用します.自分のdidSetオブジェクトのプロパティに値を割り当てると、設定した値が新しい値に置き換えられます.
    class StepCounter {
        var totalSteps: Int = 0 {
            willSet(newTotalSteps) {
                print("About to set totalSteps to \(newTotalSteps)")
            }
            didSet {
                if totalSteps > oldValue  {
                    print("Added \(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観察者は常に呼び出されます.これは、入出力パラメータのコピー入出力メモリモデルに使用されるためです.この値は、関数の末尾にある属性に常に書き込まれます.入力-出力パラメータの動作の詳細については、入力-出力パラメータを参照してください.