Swift 5.3 日本語化計画 : Swift 5.3


プロパティ


プロパティ は、特定のクラス、構造体、または列挙型と値を関連付けます。計算されたプロパティが (むしろ格納よりも) 値を計算するのに対して、格納されたプロパティは、インスタンスの一部として定数と変数の値を格納します。計算されたプロパティは、クラス、構造体、列挙型によって提供されます。格納されたプロパティはクラスおよび構造体によってのみ提供されます。


格納されたプロパティと計算されたプロパティは、通常、特定の型のインスタンスと関連付けられています。しかし、プロパティはまた、型自体に関連付けられることもできます。このようなプロパティは、型のプロパティとして知られています。


加えて、カスタム・アクションと共に応答できるプロパティの値の変化を監視するプロパティ監視者を定義できます。プロパティ監視者は、あなた自身が定義する格納されたプロパティに追加することができ、サブクラスがそのスーパークラスから継承するプロパティにも、追加できます。



格納されたプロパティ


その最も単純な形態では、格納されたプロパティは、特定のクラスまたは構造体のインスタンスの一部として格納されている定数または変数です。格納されたプロパティは、(var キーワードに引き続き定義された) 変数に格納されたプロパティ または (let キーワードに引き続き定義された) 定数に格納されたプロパティ のいずれかになります。


デフォルトのプロパティ値 で説明したように、その定義の一部として格納されたプロパティのデフォルト値を提供できます。また、初期化中に、格納されたプロパティの初期値を設定し、変更する事もできます。初期化中に定数プロパティへの代入 で説明したように、これは定数に格納されたプロパティについても正しいです。


以下の例では、FixedLengthRange という構造体を定義し、一旦作成されたら変更できない範囲の長さの整数の範囲を記述しています。


  1. struct FixedLengthRange {
  2. var firstValue: Int
  3. let length: Int
  4. }
  5. var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
  6. // the range represents integer values 0, 1, and 2
  7. rangeOfThreeItems.firstValue = 6
  8. // the range now represents integer values 6, 7, and 8


FixedLengthRange のインスタンスには、firstValue という変数に格納されたプロパティと、length という定数に格納されたプロパティがあります。上記の例では、length は新しい範囲が作成された時に初期化され、その後は、それが定数プロパティのため変更できません。


定数構造体インスタンスの格納されたプロパティ


構造体のインスタンスを作成し、定数にそのインスタンスを代入した場合、それらが変数プロパティとして宣言された場合でも、インスタンス・プロパティを変更することはできません。


  1. let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
  2. // this range represents integer values 0, 1, 2, and 3
  3. rangeOfFourItems.firstValue = 6
  4. // this will report an error, even though firstValue is a variable property


rangeOfFourItems は (let キーワードを使って) 定数として宣言されているので、firstValue が変数プロパティであっても、その firstValue プロパティを変更することはできません。


この動作は、値型 である構造体によるものです。値型のインスタンスが定数としてマークされると、そのすべてのプロパティがそのようになります。


同じことは 参照型 であるクラスの場合、正しくありません。参照型のインスタンスを定数に代入した場合、まだそのインスタンスの変数プロパティを変更することができます。


遅延した格納されたプロパティ


遅延した格納されたプロパティ は、初めて使用されるまではその初期値が計算されないプロパティです。その宣言の前に lazy の修飾子を書くことによって、遅延した格納されたプロパティを示して下さい。


注意: その初期値が、インスタンスの初期化が完了するまで取得できないので、常に、(var キーワードで) 変数として遅延したプロパティを宣言しなければなりません。定数プロパティは、初期化が完了する 前に、必ず値を持たなければならず、そのため遅延した(lazy)として宣言することはできません。


遅延したプロパティは、プロパティの初期値が、インスタンスの初期化が完了するまで値がわからない外部ファクタに依存する場合に便利です。遅延したプロパティはまた、プロパティの初期値が複雑であるか計算的に高価なセットアップを必要とする場合にも有用で、それが必要とされない限り、実行されません。


以下の例は、複雑なクラスで不必要な初期化を避けるために、遅延した格納されたプロパティを使用しています。この例では、2つとも完全には示されていない DataImporterDataManager と言う2つのクラスを定義しています。


  1. class DataImporter {
  2. /*
  3. DataImporter is a class to import data from an external file.
  4. The class is assumed to take a non-trivial amount of time to initialize.
  5. */
  6. var fileName = "data.txt"
  7. // the DataImporter class would provide data importing functionality here
  8. }
  9. class DataManager {
  10. lazy var importer = DataImporter()
  11. var data = [String]()
  12. // the DataManager class would provide data management functionality here
  13. }
  14. let manager = DataManager()
  15. manager.data.append("Some data")
  16. manager.data.append("Some more data")
  17. // the DataImporter instance for the importer property has not yet been created


DataManager クラスには、String 値の新しい、空の配列で初期化された data と言う格納されたプロパティがあります。その機能の残りの部分は示されていませんが、この DataManagaer クラスの目的は String データのこの配列へのアクセスを管理し、提供することにあります。


DataManager クラスの機能の一部は、ファイルからデータをインポートする機能です。この機能は、 DataImporter クラスによって提供され、初期化するためには少なくない時間が掛かると仮定されます。これは、 DataImporter インスタンスがファイルを開いて、DataImporter インスタンスが初期化されたときにその内容をメモリに読み込む必要があるからです。


DataManager インスタンスは、ファイルからデータをインポートすることが全くなく、そのデータを管理することが可能であるため、DataManager 自体が作成されても、新しいDataImporter インスタンスを作成する必要はありません。その代わりに、それを最初に使用する際 DataImporter インスタンスを作成するのが、より理にかなっています。


lazy 修飾子でマークされているため、その fileName プロパティが照会されたときのように、importer プロパティが最初にアクセスされたときにのみ importer プロパティの DataImporter インスタンスは作成されます。


  1. print(manager.importer.fileName)
  2. // the DataImporter instance for the importer property has now been created
  3. // prints "data.txt"


注意: lazy 修飾子でマークされたプロパティに複数のスレッドが同時にアクセスし、そのプロパティがまだ初期化されていない場合、そのプロパティが一度しか初期化されないという保証はありません。


格納されたプロパティとインスタンス変数


Objective-C の経験がある場合、それがクラスインスタンスの一部として値と参照を格納するために 2つ の方法を提供することを知っているかもしれません。プロパティに加えて、プロパティに格納された値の補助記憶としてインスタンス変数を使用できます。


Swift は、これらの概念を一つのプロパティ宣言に統合します。Swift のプロパティは、対応するインスタンス変数を持っておらず、プロパティの補助記憶には直接アクセスできません。このアプローチは、どう値が異なる文脈でアクセスされたかについての混乱を回避し、一つの、定義文にプロパティの宣言を簡素化します。プロパティに関するすべての情報は、その名前、型、およびメモリ管理特性を含め、型の定義の一部として一つの場所で定義されています。



計算されたプロパティ


格納されたプロパティ、クラス、構造体、および列挙型に加えて、実際には値を格納しない、計算されたプロパティ を定義できます。代わりに、それらは間接的に他のプロパティと値を取得し設定するためのゲッタと optional のセッタを提供します。


  1. struct Point {
  2. var x = 0.0, y = 0.0
  3. }
  4. struct Size {
  5. var width = 0.0, height = 0.0
  6. }
  7. struct Rect {
  8. var origin = Point()
  9. var size = Size()
  10. var center: Point {
  11. get {
  12. let centerX = origin.x + (size.width / 2)
  13. let centerY = origin.y + (size.height / 2)
  14. return Point(x: centerX, y: centerY)
  15. }
  16. set(newCenter) {
  17. origin.x = newCenter.x - (size.width / 2)
  18. origin.y = newCenter.y - (size.height / 2)
  19. }
  20. }
  21. }
  22. var square = Rect(origin: Point(x: 0.0, y: 0.0),
  23. size: Size(width: 10.0, height: 10.0))
  24. let initialSquareCenter = square.center
  25. square.center = Point(x: 15.0, y: 15.0)
  26. print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
  27. // prints "square.origin is now at (10.0, 10.0)"


この例では、幾何学的形状を操作するため3つの構造体を定義しています。


Rect 構造体はまた、center と言う計算されたプロパティも提供します。Rect の現在の中心位置は、常にその originsize から決定できるので、明示的な Point 値として中心点を格納する必要はありません。その代わり、Rect は、それが本当の格納されたプロパティであるかのように長方形の center で作業できるよう、center と言う計算された変数のカスタムゲッタとセッタを定義します。


上述の例では、square と言う新しい Rect 変数を作成しています。square 変数は (0,0) の原点で初期化され、幅及び高さは 10 です。この正方形は、下の図の青い正方形で表されています。


square 変数の center プロパティは、現在のプロパティ値を取得するために、ドット構文 (square.center) を介してアクセスされ、center のゲッタの呼び出しを引き起こします。既存の値を返すよりもむしろ、ゲッタは、実際に計算し、正方形の中心を表すために新しい Point を返します。上記から分かるように、ゲッタは正しく (5,5) の中心点を返します。


center プロパティは、下の図ではオレンジ色の正方形で示される新しい位置に、正方形を上、右にに移動し、(15、15) の新しい値に設定されます。center プロパティを設定すると、格納された origin プロパティの xy の値を変更する center 用セッタを呼び出し、その新しい位置に正方形を移動します。



computedProperties_2x


セッタ宣言の省略形


計算されたプロパティのセッタが、設定すべき新しい値の名前を定義していなければ、デフォルトの名前である newValue が使用されます。ここで、この省略形の表記法を利用している、Rect 構造体の別バージョンを示します:


  1. struct AlternativeRect {
  2. var origin = Point()
  3. var size = Size()
  4. var center: Point {
  5. get {
  6. let centerX = origin.x + (size.width / 2)
  7. let centerY = origin.y + (size.height / 2)
  8. return Point(x: centerX, y: centerY)
  9. }
  10. set {
  11. origin.x = newValue.x - (size.width / 2)
  12. origin.y = newValue.y - (size.height / 2)
  13. }
  14. }
  15. }


ゲッタ宣言の省略形


ゲッタ (getter) の本体全体が単一の式である場合、ゲッタはその式を暗黙的に返します。この省略表記とセッタ (setter) の省略表記を利用する Rect 構造体の別のバージョンを次に示します。


  1. struct CompactRect {
  2. var origin = Point()
  3. var size = Size()
  4. var center: Point {
  5. get {
  6. Point(x: origin.x + (size.width / 2),
  7. y: origin.y + (size.height / 2))
  8. }
  9. set {
  10. origin.x = newValue.x - (size.width / 2)
  11. origin.y = newValue.y - (size.height / 2)
  12. }
  13. }
  14. }


ゲッタからの return を省略することは、暗黙の戻り値を持つ関数 で説明したように、関数からの return を省略することと同じ規則に従います。


読み取り専用の計算されたプロパティ


ゲッタと計算されたプロパティがあり、セッタがないのは 読み取り専用の計算されたプロパティ として知られています。読み取り専用の計算されたプロパティは常に値を返し、ドット構文を介してアクセスできますが、別の値に設定することはできません。


注意: それらの値が固定されていないので、計算されたプロパティを、読み取り専用の計算されたプロパティも含め、var キーワードで変数プロパティとして宣言しなければなりません。let キーワードは、インスタンスの初期化の一部として、一度設定されるとその値を変更できないことを示すために、定数のプロパティのためにのみ使用されます。


get キーワードとその括弧を除くと、読み取り専用の計算されたプロパティの宣言を単純化できます。


  1. struct Cuboid {
  2. var width = 0.0, height = 0.0, depth = 0.0
  3. var volume: Double {
  4. return width * height * depth
  5. }
  6. }
  7. let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
  8. print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
  9. // prints "the volume of fourByFiveByTwo is 40.0"


この例では、widthheight、そして depth プロパティがある三次元の長方形の箱を表す Cuboid と言う新しい構造体を定義しています。この構造体にはまた、volume と言う読み取り専用の計算されたプロパティもあり、立方体の現在の体積を計算して返します。widthheight そして depth のどの値が、特定の volume の値に使用されるかに関してあいまいなので、volume が設定可能である事をこれは意味しません。それにも関わらず、Cuboid が現在の計算された体積を外部ユーザーが発見できるようにするために読み取り専用の計算されたプロパティを提供するのは、便利です。



プロパティ監視者


プロパティ監視者は、プロパティの値の変化を監視し、対応します。プロパティ監視者は、新しい値がプロパティの現在の値と同じであっても、プロパティの値が設定されるたびに呼びだされます。


以下の場所にプロパティ監視者を追加できます。


継承されたプロパティの場合、サブクラスでそのプロパティをオーバーライドすることにより、プロパティ監視者を追加します。定義した計算されたプロパティの場合、監視者を作成しようとする代わりに、プロパティのセッタを使用して値の変更を監視し、応答します。プロパティのオーバーライドについては、オーバーライド(上書き) で説明しています。


プロパティのこれらの監視者のいずれかまたは両方を定義するオプションがあります:


willSet 監視者を実装する場合、それには定数パラメータとして新しいプロパティ値が渡されます。willSet の実装の一部として、このパラメータの名前を指定できます。実装内でパラメータ名と括弧を書かない場合、パラメータは newValue のデフォルトのパラメータ名で利用できるようになります。


同様に、didSet 監視者を実装した場合、それには古いプロパティ値を含む定数パラメータが渡されます。パラメータに名前を付けるか、またはは oldValue のデフォルトのパラメータ名を使用できます。独自の didSet 監視者内のプロパティに値を割り当てる場合は、割り当てた新しい値は、ちょうど設定されたものを置き換えます。


注意: プロパティがサブクラスのイニシャライザ内で設定されている時、スーパークラスのイニシャライザが呼び出された後、スーパークラスのプロパティの willSetdidSet の監視者プロパティが、呼び出されます。スーパークラスのイニシャライザが呼び出される前に、クラスが独自のプロパティを設定している間、それらは呼び出されません。

イニシャライザのデリゲートの詳細については、値型のイニシャライザデリゲート と、クラス型のイニシャライザデリゲート を参照してください。


ここでアクションの willSetdidSet の例を挙げます。以下の例では、歩く時に人が歩くステップの総数を追跡する StepCounter と言う新しいクラスを定義しています。このクラスは、彼らの日常生活の間に、人の運動を追跡するために、歩数計またはその他の歩数カウンターからの入力データで使用されるかもしれません。


  1. class StepCounter {
  2. var totalSteps: Int = 0 {
  3. willSet(newTotalSteps) {
  4. print("About to set totalSteps to \(newTotalSteps)")
  5. }
  6. didSet {
  7. if totalSteps > oldValue {
  8. print("Added \(totalSteps - oldValue) steps")
  9. }
  10. }
  11. }
  12. }
  13. let stepCounter = StepCounter()
  14. stepCounter.totalSteps = 200
  15. // About to set totalSteps to 200
  16. // Added 200 steps
  17. stepCounter.totalSteps = 360
  18. // About to set totalSteps to 360
  19. // Added 160 steps
  20. stepCounter.totalSteps = 896
  21. // About to set totalSteps to 896
  22. // Added 536 steps


StepCounter クラスは、Int 型の totalSteps プロパティを宣言します。これは willSetdidSet 監視者のある格納されたプロパティです。


プロパティが新しい値を代入された時はいつでも totalStepswillSetdidSet 監視者が呼び出されます。これは、新しい値が現在の値と同じ場合でも同様です。


この例の willSet 監視者は、今後の新しい値のために newTotalSteps と言うカスタムパラメータ名を使用します。この例では、設定しようとする値を単に印刷します。


totalSteps の値が更新された後 didSet 監視者が呼び出されます。それは、古い値に対して totalSteps の新しい値を比較します。総ステップ数が増加していた場合、どれだけ多く新たなステップが踏まれたかを示すメッセージが印刷されます。didSet 監視者は、古い値にカスタムパラメータ名を提供せず、代わりに oldValue のデフォルト名が使用されます。


注意: in-out パラメータとして関数に、監視者を持つプロパティを渡すと、willSetdidSet 監視者が常に呼び出されます。これは、in-out パラメータの copy-in copy-out メモリモデルのためです。値は常に関数の最後に、プロパティに書き戻されます。in-out パラメータの動作についての詳細は、In-Out パラメータ を参照してください。


プロパティラッパー


プロパティラッパーは、プロパティの格納方法を管理するコードとプロパティを定義するコードの間に分離層を追加します。たとえば、スレッドセーフのチェックを提供するプロパティがある場合、または基になるデータをデータベースに格納する場合、すべてのプロパティにそのコードを記述しなければなりません。プロパティラッパーを使用する場合は、ラッパーを定義するときに管理コードを 1 回記述すれば、それを複数のプロパティに適用して管理コードをその時再利用できます。


プロパティラッパーを定義するには、wrappedValue プロパティを定義する構造体、列挙型、またはクラスを作成して下さい。以下のコードでは、TwelveOrLess 構造体により、ラップする値に常に 12 以下の数値が含まれることが保証されます。より大きい数値を格納するように要求すると、代わりに 12 が格納されます。


  1. @propertyWrapper
  2. struct TwelveOrLess {
  3. private var number: Int
  4. init() { self.number = 0 }
  5. var wrappedValue: Int {
  6. get { return number }
  7. set { number = min(newValue, 12) }
  8. }
  9. }


セッタは新しい値が 12 未満であることを保証し、ゲッタは格納された値を返します。


注意:

上記の例の number の宣言は、変数を private としてマークしており、これにより TwelveOrLess の実装でのみ number が使用される事を保証しています。他の場所で記述されたコードは、wrappedValue のゲッタとセッタを使用して値にアクセスし、number を直接使用することはできません。private の詳細については、アクセス制御 を参照してください。




属性としてプロパティの前にラッパーの名前を書くことで、プロパティにラッパーを適用します。 TwelveOrLess プロパティラッパーによって実装される "small" の同じ (むしろ任意の) 定義を使用して、小さな長方形を格納する構造体は次のとおりです。


  1. struct SmallRectangle {
  2. @TwelveOrLess var height: Int
  3. @TwelveOrLess var width: Int
  4. }
  5. var rectangle = SmallRectangle()
  6. print(rectangle.height)
  7. // Prints "0"
  8. rectangle.height = 10
  9. print(rectangle.height)
  10. // Prints "10"
  11. rectangle.height = 24
  12. print(rectangle.height)
  13. // Prints "12"


height プロパティと width プロパティは、TwelveOrLess.number をゼロに設定する TwelveOrLess の定義から初期値を取得します。数字 10 は、小さい数字であるため、rectangle.height への保存は成功します。24 はプロパティのセッタの規則では大きすぎるため、24 を保存しようとすると、実際には 12 の値が代わりに保存されます。


ラッパーをプロパティに適用すると、コンパイラはラッパーに保管場所を提供するコードと、ラッパーを介してプロパティへのアクセスを提供するコードを合成します。(プロパティラッパーはラップされた値を格納するのに責任があるため、その合成コードはありません。) 特別な属性構文を利用せずに、プロパティラッパーの動作を使用するコードを記述できます。たとえば、属性として @TwelveOrLess を記述する代わりに、 TwelveOrLess 構造体のプロパティを明示的にラップする以前のコードリストの SmallRectangle のバージョンは次のとおりです。


  1. struct SmallRectangle {
  2. private var _height = TwelveOrLess()
  3. private var _width = TwelveOrLess()
  4. var height: Int {
  5. get { return _height.wrappedValue }
  6. set { _height.wrappedValue = newValue }
  7. }
  8. var width: Int {
  9. get { return _width.wrappedValue }
  10. set { _width.wrappedValue = newValue }
  11. }
  12. }


_height プロパティと _width プロパティには、プロパティラッパーのインスタンス TwelveOrLess が格納されます。heightwidth のゲッタとセッタは、wrappedValue プロパティへのアクセスをラップします。


ラップされたプロパティの初期値の設定


上記の例のコードは、TwelveOrLess の定義で初期値に number を指定することにより、ラップされたプロパティの初期値を設定します。このプロパティラッパーを使用するコードは、TwelveOrLess でラップされるプロパティに異なる初期値を指定できません。たとえば、SmallRectangle の定義では、height または width の初期値を指定できません。初期値の設定やその他のカスタマイズをサポートするには、プロパティラッパーはイニシャライザを追加する必要があります。次に、SmallNumber と呼ばれる TwelveOrLess の拡張バージョンを示します。これは、ラップされた値と最大値を設定するイニシャライザを定義します。


  1. @propertyWrapper
  2. struct SmallNumber {
  3. private var maximum: Int
  4. private var number: Int
  5. var wrappedValue: Int {
  6. get { return number }
  7. set { number = min(newValue, maximum) }
  8. }
  9. init() {
  10. maximum = 12
  11. number = 0
  12. }
  13. init(wrappedValue: Int) {
  14. maximum = 12
  15. number = min(wrappedValue, maximum)
  16. }
  17. init(wrappedValue: Int, maximum: Int) {
  18. self.maximum = maximum
  19. number = min(wrappedValue, maximum)
  20. }
  21. }


SmallNumber の定義には、init()、init(wrappedValue:)、および init(wrappedValue:maximum:) の 3 つのイニシャライザが含まれています。これらを使用して、以下の例ではラップされた値と最大値を設定します。初期化とイニシャライザの構文の詳細については、初期化 を参照してください。


ラッパーをプロパティに適用し、初期値を指定しない場合、Swift は init() イニシャライザを使用してラッパーをセットアップします。例えば:


  1. struct ZeroRectangle {
  2. @SmallNumber var height: Int
  3. @SmallNumber var width: Int
  4. }
  5. var zeroRectangle = ZeroRectangle()
  6. print(zeroRectangle.height, zeroRectangle.width)
  7. // Prints "0 0"


SmallNumber() を呼び出すことにより、heightwidth をラップする SmallNumber のインスタンスが作成されます。イニシャライザ内のコードは、ゼロと 12 のデフォルト値を使用して、初期のラップされた値と初期最大値を設定します。SmallRectangle 内で TwelveOrLess を使用した以前の例のように、プロパティのラッパーはすべての初期値をなお提供します。その例とは異なり、SmallNumbe はまた、プロパティの宣言の一部としてこれらの初期値の書き込みもサポートしています。


プロパティの初期値を指定すると、Swift は init(wrappedValue:) イニシャライザを使用してラッパーをセットアップします。例えば:


  1. struct UnitRectangle {
  2. @SmallNumber var height: Int = 1
  3. @SmallNumber var width: Int = 1
  4. }
  5. var unitRectangle = UnitRectangle()
  6. print(unitRectangle.height, unitRectangle.width)
  7. // Prints "1 1"


ラッパーを使用してプロパティ上に = 1 と記述すると、それは init(wrappedValue:) イニシャライザへの呼び出しに変換されます。heightwidth をラップする SmallNumber のインスタンスは、 SmallNumber(wrappedValue:1) を呼び出すことによって作成されます。イニシャライザは、ここで指定されたラップされた値を使用し、デフォルトの最大値の 12 を使用します。


カスタム属性の後に括弧で引数を記述すると、Swift はそれらの引数を受け入れるイニシャライザを使用してラッパーをセットアップします。たとえば、初期値と最大値を指定すると、Swift は init(wrappedValue:maximum:) イニシャライザを使用します。


  1. struct NarrowRectangle {
  2. @SmallNumber(wrappedValue: 2, maximum: 5) var height: Int
  3. @SmallNumber(wrappedValue: 3, maximum: 4) var width: Int
  4. }
  5. var narrowRectangle = NarrowRectangle()
  6. print(narrowRectangle.height, narrowRectangle.width)
  7. // Prints "2 3"
  8. narrowRectangle.height = 100
  9. narrowRectangle.width = 100
  10. print(narrowRectangle.height, narrowRectangle.width)
  11. // Prints "5 4"


height をラップする SmallNumber のインスタンスは SmallNumber(wrappedValue: 2、maximum:5) を呼び出すことによって作成され、width をラップするインスタンスは SmallNumber(wrappedValue: 3、maximun: 4) を呼び出すことによって作成されます。

プロパティラッパーに引数を含めることにより、ラッパーで初期状態を設定したり、ラッパーの作成時に他のオプションを渡すことができます。この構文は、プロパティラッパーを使用する最も一般的な方法です。属性に必要な全ての引数を指定すると、それらがイニシャライザに渡されます。


プロパティラッパー引数を含める場合、割り当てを使用して初期値を指定することもできます。Swift は代入を wrappedValue 引数のように扱い、含める引数を受け入れるイニシャライザを使用します。例えば:


  1. struct MixedRectangle {
  2. @SmallNumber var height: Int = 1
  3. @SmallNumber(maximum: 9) var width: Int = 2
  4. }
  5. var mixedRectangle = MixedRectangle()
  6. print(mixedRectangle.height)
  7. // Prints "1"
  8. mixedRectangle.height = 20
  9. print(mixedRectangle.height)
  10. // Prints "12"


height をラップする SmallNumber のインスタンスは、SmallNumber(wrappedValue: 1) を呼び出して作成され、これは、デフォルトの最大値 12 を使用します。width をラップするインスタンスは、SmallNumber(wrappedValue: 2、maximum: 9) を呼び出して作成されます。


プロパティラッパーからの値の投影


ラップされた値に加えて、プロパティラッパーは、投影値 を定義することで追加機能を公開できます。たとえば、データベースへのアクセスを管理するプロパティラッパーは、投影値で flushDatabaseConnection() メソッドを公開できます。投影された値の名前は、ドル記号 ($) で始まることを除いて、ラップされた値と同じです。コードでは $ で始まるプロパティを定義できないため、投影値は定義したプロパティに干渉することは決してありません。


上記の SmallNumber の例で、プロパティを大きすぎる数値に設定しようとすると、プロパティラッパーは数値を調整してから格納します。以下のコードは、projectedValue プロパティをSmallNumber 構造体に追加して、プロパティが新しい値を保存する前にプロパティラッパーの新しい値を調整したかどうかを追跡します。


  1. @propertyWrapper
  2. struct SmallNumber {
  3. private var number: Int
  4. var projectedValue: Bool
  5. init() {
  6. self.number = 0
  7. self.projectedValue = false
  8. }
  9. var wrappedValue: Int {
  10. get { return number }
  11. set {
  12. if newValue > 12 {
  13. number = 12
  14. projectedValue = true
  15. } else {
  16. number = newValue
  17. projectedValue = false
  18. }
  19. }
  20. }
  21. }
  22. struct SomeStructure {
  23. @SmallNumber var someNumber: Int
  24. }
  25. var someStructure = SomeStructure()
  26. someStructure.someNumber = 4
  27. print(someStructure.$someNumber)
  28. // Prints "false"
  29. someStructure.someNumber = 55
  30. print(someStructure.$someNumber)
  31. // Prints "true"


someStructure.$someNumber と書くと、ラッパーの投影値にアクセスします。4 などの小さな数値を格納した後、 someStructure.$someNumber の値は false です。ただし、55 などの大きすぎる数値を保存しようとすると、投影値は true になります。


プロパティラッパーは、その投影値として任意の型の値を返すことができます。この例では、プロパティラッパーは 1 つの情報 (数値が調整されたかどうか) のみを公開するので、そのブール値を投影値として公開します。より多くの情報を公開する必要があるラッパーは、他のデータ型のインスタンスを返すか、self を返して、ラッパーのインスタンスをその投影値として公開できます。


プロパティのゲッタやインスタンスメソッドなど、型の一部であるコードから投影値にアクセスする場合、他のプロパティにアクセスするのと同じように、プロパティ名の前の self を省略できます。以下の例のコードは、heightwidth の周りのラッパーの投影値を $height$width として参照しています。


  1. enum Size {
  2. case small, large
  3. }
  4. struct SizedRectangle {
  5. @SmallNumber var height: Int
  6. @SmallNumber var width: Int
  7. mutating func resize(to size: Size) -> Bool {
  8. switch size {
  9. case .small:
  10. height = 10
  11. width = 20
  12. case .large:
  13. height = 100
  14. width = 100
  15. }
  16. return $height || $width
  17. }
  18. }


プロパティラッパー構文は、ゲッタとセッタを持つプロパティの単なる構文上のシュガーなので、heightwidth にアクセスすることは、他のプロパティにアクセスするのと同じように動作します。たとえば、resize(to:) のコードは、それらのプロパティラッパーを使用して heightwidth にアクセスします。 resize(to: .large) を呼び出すと、.large の switch の case は、長方形の高さと幅を 100 に設定します。ラッパーは、これらのプロパティの値が 12 を超えないようにし、投影値を true に設定し、値を調整したという事実を記録します。resize(to:) の最後で、return 文は $height$width をチェックして、プロパティラッパーが height または width を調整したかどうかを判断します。


グローバルとローカルの変数


計算する、監視するプロパティの、上記で説明した機能はまた グローバル変数ローカル変数 にも利用可能です。グローバル変数は全ての関数、メソッド、クロージャ、または型のコンテキストの外で定義されている変数です。ローカル変数は、関数、メソッド、またはクロージャのコンテキスト内で定義されている変数です。


以前の章で遭遇したグローバルとローカル変数は、すべて 格納された変数 です。格納された変数は、格納されたプロパティと同様に、特定の型の値の記憶域を提供し、その値を設定し、取得することを可能にします。


ただし、計算された変数 を定義することもできますし、グローバルまたはローカルの範囲で、格納された変数の監視者も定義できます。計算された変数は、値を格納するよりも計算し、計算されたプロパティと同じように書かれます。


注意: グローバル定数と変数は常に 遅延して格納されたプロパティ と同様に、遅延して計算されます。遅延して格納されたプロパティとは異なり、グローバル定数と変数は lazy 修飾子でマークする必要はありません。

ローカル定数や変数は遅延して計算されることは決してありません。


型プロパティ


インスタンス・プロパティは、特定の型のインスタンスに属しているプロパティです。その型の新しいインスタンスを作成するたびに、それは他のインスタンスとは別のプロパティ値の独自のセットを持っています。


また、その型のいずれのインスタンスにでもない、型自体に属しているプロパティを定義することもできます。その型のインスタンスをどれだけ多くあなたが作成したかに関係なく、これらのプロパティのいずれか一つのコピーしかありません。これらの種類のプロパティは、型プロパティ と呼ばれます。


型プロパティは、特定の型の すべての インスタンスへの普遍的な値を定義し、すべてのインスタンスが (C の静的定数のように) 使える定数プロパティのように、またはその型の全てのインスタンスに対するグローバルな値を格納する (C の静的変数のように) 変数プロパティとして、有用です。


格納された型プロパティは、変数または定数にすることができます。計算された型プロパティは常に計算されたインスタンスのプロパティと同じように、変数プロパティとして宣言されます。



注意: 格納されたインスタンスプロパティとは異なり、格納された型プロパティには常にデフォルト値を与えなければなりません。これは、型自体は、初期化される時には格納された型プロパティに値を代入できるイニシャライザを持っていないためです。

格納された型プロパティはその最初のアクセスで遅延して初期化されます。これらは、同時に複数のスレッドによってアクセスされる場合であっても、一度だけ初期化されることが保証されており、それらは lazy 修飾子でマークする必要はありません。


型プロパティの構文


C 言語 と Objective-C では、グローバル 静的変数として型に関連した静的定数と変数を定義できます。Swift では、しかし、型プロパティは、型の外の中括弧の中に、型の定義の一部として書かれ、各々の型プロパティはサポートしている型に明示的にスコープされます。


static キーワードで型プロパティを定義して下さい。クラス型の計算された型プロパティでは、スーパークラスの実装をサブクラスがオーバーライド(上書き)するのを許す代わりに class キーワードを使用できます。以下の例で、格納された型プロパティと、計算された型プロパティの構文を示します。


  1. struct SomeStructure {
  2. static var storedTypeProperty = "Some value."
  3. static var computedTypeProperty: Int {
  4. return 1
  5. }
  6. }
  7. enum SomeEnumeration {
  8. static var storedTypeProperty = "Some value."
  9. static var computedTypeProperty: Int {
  10. return 6
  11. }
  12. }
  13. class SomeClass {
  14. static var storedTypeProperty = "Some value."
  15. static var computedTypeProperty: Int {
  16. return 27
  17. }
  18. class var overrideableComputedTypeProperty: Int {
  19. return 107
  20. }
  21. }


注意: 上記の計算された型プロパティの例では、読み取り専用の計算された型プロパティですが、計算されたインスタンス・プロパティと同じ構文で、読み書きのできる、計算された型プロパティも定義できます。


型プロパティの照会と設定


型プロパティは、ちょうどインスタンス・プロパティのように、ドット構文で照会、設定できます。しかし、型プロパティはその型のインスタンスではなく、 により照会、設定されます。例えば:


  1. print(SomeClass.computedTypeProperty)
  2. // prints "Some value"
  3. SomeStructure.storedTypeProperty = "Another value."
  4. print(SomeStructure.storedTypeProperty)
  5. // prints "Another value."
  6. print(SomeEnumeration.computedTypeProperty)
  7. // Prints "6"
  8. print(SomeClass.computedTypeProperty)
  9. // prints "27"


以下の例は、オーディオチャンネル数のオーディオレベルメーターをモデルにした構造体の一部として2つの格納された型プロパティを使用しています。各チャンネルには、0 から 10 までの整数のオーディオレベルがあります。


下の図は、これら2つのオーディオチャンネルが、ステレオのオーディオ・レベルメーターをモデル化して組み合わせられる所を示しています。チャンネルのオーディオレベルが 0 の場合、そのチャンネルの光はいずれも点灯しません。オーディオレベルが 10 の場合、そのチャンネルの光は全て点灯します。この図では、左チャンネルは現在 9 のレベルであり、右チャンネルは現在 7 のレベルです。


staticPropertiesVUMeter_2x


上述のオーディオチャンネルを AudioChannel 構造体のインスタンスによって表わします。


  1. struct AudioChannel {
  2. static let thresholdLevel = 10
  3. static var maxInputLevelForAllChannels = 0
  4. var currentLevel: Int = 0 {
  5. didSet {
  6. if currentLevel > AudioChannel.thresholdLevel {
  7. // cap the new audio level to the threshold level
  8. currentLevel = AudioChannel.thresholdLevel
  9. }
  10. if currentLevel > AudioChannel.maxInputLevelForAllChannels {
  11. // store this as the new overall maximum input level
  12. AudioChannel.maxInputLevelForAllChannels = currentLevel
  13. }
  14. }
  15. }
  16. }


AudioChannel 構造体は、その機能をサポートするために、2つの格納された型プロパティを定義しています。まず、thresholdLevel は、オーディオレベルが取りうる最大の閾値を定義しています。これは、すべての AudioChannel インスタンスに対する 10 の定数値です。オーディオ信号が 10 よりも高い値になる場合には、この閾値にフタをされます (後述)。


第二の型プロパティは、maxInputLevelForAllChannels という変数に格納されたプロパティです。これは、全てAudioChannel インスタンスによって受信された最大入力値を追跡します。これは、初期値 0 で始まります。


AudioChannel 構造体はまた、0 から 10 のスケールでチャンネルの現在のオーディオレベルを表す currentLevel という、格納されたインスタンスプロパティも定義しています。


currentLevel プロパティには、それが設定されるたびに currentLevel の値を確認する didSet プロパティの監視者があります。この監視者は2つのチェックを実行します。


注意: これら二つのチェックのうち最初の項目では、didSet 監視者は currentLevel を違う値に設定します。しかしこれで、監視者が再び呼び出されることはありません。


ステレオサウンドシステムのオーディオレベルを表すために、leftChannelrightChannel という二つの新しいオーディオチャンネルを作成するために AudioChannel 構造体を使用します。


  1. var leftChannel = AudioChannel()
  2. var rightChannel = AudioChannel()


チャンネルの currentLevel7 に設定すると、maxInputLevelForAllChannels の型プロパティが 7 に等しく更新されていることがわかるでしょう。


  1. leftChannel.currentLevel = 7
  2. print(leftChannel.currentLevel)
  3. // prints "7"
  4. print(AudioChannel.maxInputLevelForAllChannels)
  5. // prints "7"


チャンネルの currentLevel11 を設定しようとすると、右チャンネルの currentLevel プロパティが 10 の最大値でフタをされ、maxInputLevelForAllChannels 型プロパティは、10 に等しく更新されます。


  1. rightChannel.currentLevel = 11
  2. print(rightChannel.currentLevel)
  3. // prints "10"
  4. print(AudioChannel.maxInputLevelForAllChannels)
  5. // prints "10"

前:構造体とクラス 次:メソッド
















トップへ












トップへ












トップへ












トップへ
目次
Xcode の新機能

Swift について
Swift と Cocoa と Objective-C (obsolete)
Swift Blog より (obsolete)

SwiftLogo
  • Swift 5.3 全メニュー


  • Swift へようこそ
  • Swift について
  • Swift 言語のガイド
  • Swift の基本
  • 基本演算子
  • 文字列と文字
  • コレクション型
  • フロー制御
  • 関数
  • クロージャ
  • 列挙型
  • 構造体とクラス
  • プロパティ
  • メソッド
  • サブスクリプト
  • 継承
  • 初期化
  • デイニシャライザ
  • Optional の連鎖
  • エラー処理
  • 型キャスト
  • ネストした型
  • 拡張機能
  • プロトコル
  • ジェネリック(汎用)
  • 不透明な型
  • 自動参照カウント
  • メモリの安全性
  • アクセス制御
  • 高度な演算子

  • 言語リファレンス

  • マニュアルの変更履歴













  • トップへ












    トップへ












    トップへ












    トップへ












    トップへ












    トップへ












    トップへ












    トップへ












    トップへ












    トップへ