Swiftプログラミング言語学習9-ストレージ属性と計算属性


プロパティは、値を特定のクラス、構造、または列挙に関連付けます.ストレージ属性は、インスタンスの一部として定数または変数を格納し、ストレージではなく属性計算の値を計算します.計算プロパティはクラス、構造体、列挙に使用できます.ストレージプロパティはクラスおよび構造体にのみ使用できます.
 
ストレージ・プロパティと計算プロパティは、通常、特定のタイプのインスタンスに使用されますが、タイプ自体にも直接使用できます.このプロパティをタイプ・プロパティと呼びます.
 
また、プロパティモニタを定義してプロパティ値の変化を監視し、カスタム操作をトリガーすることもできます.プロパティモニタは、自分が書いたストレージプロパティに追加したり、親から継承したプロパティに追加したりできます.
 
 
ストレージのプロパティ
簡単に言えば、1つのストレージ属性は、特定のクラスまたは構造体のインスタンスに格納される定数または変数であり、ストレージ属性は変数ストレージ属性(キーワードvarで定義)であってもよいし、定数ストレージ属性(キーワードletで定義)であってもよい.
 
ストレージ属性を定義するときにデフォルト値を指定できます.構築プロセスの章のデフォルト属性値の項を参照してください.コンストラクション中にストレージ属性の値を設定または変更したり、定数ストレージ属性の値を変更したりすることもできます.コンストラクションプロセスの章の初期化フェーズで定数ストレージ属性を変更するセクションを参照してください.
 
次の例では、作成後に値ドメイン幅を変更できない区間を記述するFixedLengthRangeという構造体を定義します.
 
struct FixedLengthRange {
   var firstValue: Int
   let length: Int
}
var rangeOfThreeItems =FixedLengthRange(firstValue: 0, length: 3)
//  0,1,2
rangeOfThreeItems.firstValue = 6
//  6,7,8

FixedLengthRangeのインスタンスにはfirstValueという変数格納属性とlengthという定数格納属性が含まれます.上記の例では、lengthはインスタンスを作成するときに定数ストレージ属性であるため、後で値を変更することはできません.
 
 
定数とストレージのプロパティ
構造体のインスタンスを作成して定数に値を割り当てた場合、変数ストレージ属性が定義されている場合でも、インスタンスの属性は変更できません.
 
let rangeOfFourItems =FixedLengthRange(firstValue: 0, length: 4)
//  0,1,2,3
rangeOfFourItems.firstValue = 6
//  firstValue , 

rangeOfFourItemsが定数(letキーワードで)と宣言したため、firstValueが変数属性であっても変更できません.
 
この挙動は構造体(struct)が値タイプに属するためである.値タイプのインスタンスが定数として宣言されると、そのすべてのプロパティが定数になります.
 
参照タイプに属するクラス(class)は異なり、参照タイプのインスタンスを定数に割り当てると、インスタンスの変数属性を変更できます.
 
 
遅延ストレージ属性
遅延ストレージ属性とは、最初に呼び出されたときにその初期値が計算される属性です.属性宣言の前に@lazyを使用して遅延ストレージ属性を表示します.
 
注意:
 
遅延ストレージ属性は、インスタンス構築が完了するまで属性の値が得られない可能性があるため、varキーワードを使用する変数として宣言する必要があります.定数属性は、構築プロセスが完了する前に初期値が必要であるため、遅延属性として宣言できません.
遅延プロパティは、インスタンスの構築プロセスが終了する前に特定の値の外部要因が分からない場合や、プロパティの値が複雑または大量に計算される必要がある場合にのみ、必要に応じて計算できます.
 
次の例では、複雑なクラスの不要な初期化を回避するために、遅延ストレージ属性を使用します.例では、DataImporterとDataManagerの2つのクラスを定義します.次のコードの一部を示します.
 
class DataImporter {
   /*
   DataImporter  。
     。
   */
   var fileName = "data.txt"
   //  
}
 
class DataManager {
   @lazy var importer = DataImporter()
   var data = String[]()
   //  
}
 
let manager = DataManager()
manager.data += "Some data"
manager.data += "Some more data"
// DataImporter   importer  

DataManagerクラスにはdataという名前のストレージ属性があり、初期値は空の文字列(String)配列です.すべてのコードが書かれていませんが、DataManagerクラスの目的は、この文字列配列へのアクセスを管理および提供することです.
 
DataManagerの1つの機能はファイルからデータをインポートすることです.この機能はDataImporterクラスによって提供されます.DataImporterは初期化に多くの時間を費やす必要があります.インスタンスは初期化時にファイルを開く可能性があり、ファイルの内容をメモリに読み込む可能性があります.
 
DataManagerがファイルからデータをインポートしない場合もあります.したがって、DataManagerのインスタンスが作成される場合、DataImporterのインスタンスを作成する必要はありません.DataImporterが使用される場合に作成するのが賢明です.
 
@lazyが使用されているため、importerプロパティは最初にアクセスされたときにのみ作成されます.たとえば、プロパティfile Nameにアクセスする場合:
 
println(manager.importer.fileName)
// DataImporter   importer  
//   "data.txt”

 
属性とインスタンス変数の保存
Objective-Cの経験がある場合は、クラスインスタンスに値と参照を格納する方法が2つあることを知っておく必要があります.プロパティの場合、インスタンス変数をプロパティ値のバックエンドとして格納することもできます.
 
Swiftプログラミング言語ではこれらの理論を属性で統一して実現する.Swiftの属性には対応するインスタンス変数がなく,属性のバックエンドストレージにも直接アクセスできない.これにより、異なるシーンでのアクセス方法の問題を回避し、プロパティの定義を文に簡略化できます.名前、タイプ、メモリ管理フィーチャーを含む1つのタイプのプロパティのすべての情報は、ユニークな場所(タイプ定義)で定義されます.
 
 
計算プロパティ
ストレージ属性に加えて、クラス、構造体、列挙は計算属性を定義し、計算属性は直接値を格納するのではなく、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)
println("square.origin is now at(\(square.origin.x), \(square.origin.y))")
//  "square.origin is now at (10.0, 10.0)”

この例では、3つのジオメトリの構造体を定義します.
 
Pointは(x,y)の座標をカプセル化している
Sizeはwidthとheightをカプセル化した
Rectは原点と寸法のある矩形を表す
Rectにはcenterという計算プロパティも用意されています.長方形の中心点は原点と寸法から算出できるので、明示的に宣言されたPointで保存する必要はありません.Rectの計算プロパティcenterは、格納プロパティがあるように矩形の中心点を取得および設定するためにカスタムgetterおよびsetterを提供します.
 
例では次にsquareというRectインスタンスを作成し,初期値の原点は(0,0),幅の高さはいずれも10である.図に示すように青い正方形.
 
squareのcenterプロパティは、getterを呼び出してプロパティの値を取得する点演算子(square.center)でアクセスできます.既存の値を直接返すのとは異なり、getterは実際に計算して新しいPointを返すことでsquareの中心点を表します.コードに示すように、中心点(5,5)が正しく返されます.
 
センターアトリビュートの後に新しい値(15,15)が設定され、図に示すオレンジ色の正方形の位置に正方形を右上に移動することを示します.プロパティcenterの値を設定すると、setterが呼び出され、プロパティoriginのxとyの値が変更され、正方形を新しい位置に移動できます.
 
 
 
 
便利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)
    }
    }
}

 
読み取り専用計算プロパティ
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)
println("the volume of fourByFiveByTwois \(fourByFiveByTwo.volume)")
//   "the volumeof fourByFiveByTwo is 40.0"

この例では、width、height、depthプロパティを含む3 D空間を表す立方体と、volumeという読み取り専用計算プロパティを定義し、立方体のボリュームを返すために使用します.volumeの値を設定するのは意味がありません.width、height、depthでvolumeを算出できるからです.しかしながら、Cuboidは、外部ユーザが直接ボリュームを取得できるように読み取り専用の計算プロパティを提供することは有用である.