プロパティ
そもそもプロパティとは、なんぞやというところから
classやstruct,enumでよく見るこいつです。
class{
let men: String = "男"
}
このように、プロパティは型(クラス、構造体、列挙型)を構成する要素の一つで、型もしくは型のインスタンスに紐づいた値を指します。
(ちなみに、型を構成する他の要素には、メソッド・イニシャライザ・ネスト・サブスクリプトなどがあります。)
本記事では、プロパティの公式ドキュメントの内容を網羅してまとめていきます!
本記事の構成
-
Stored Properties(ストアド・プロパティ)
- Lazy Stored Propertiesz(レイジー・ストアドプロパティ)
- Computed Properties(コンピューテッド・プロパティ)
- Property Observers(プロパティオブザーバー)
- Property Wrapper(プロパティラッパー)
- Type Property
値を保有する ストアド・プロパティ
ストアド(Stored)は、「保有した」という意味です。
値には定数と変数があります。
- letキーワード:定数を保持
- varキーワード:変数の保持
struct StoredProperity{
let test: Int
var test2: String
}
上記のようなものをよく見るパターンですね。
このストアド・プロパティを細分化するとレイジー・ストアドプロパティなるものがあります。
アクセスされるまで初期化を遅延させる レイジー・ストアドプロパティ
- 初期値が、外部要因に依存している場合(インスタンスの初期化が完了するまで値がわからない)
- 初期値に不必要(or 必要になるまで実行しなくて良い)で複雑な計算量が多いセットアップをする場合
などで役に立ちます!
lazy var ~
のようにlazy修飾子をつけます。
再代入ができないletキーワードにはもちろん使えません。
class data{
var fileName = "data.txt"
}
class getData{
lazy var importer = data()
var data = [String]()
}
let lazyStoredProperty = getData() //ここでは、まだimporterは初期化されない
print(lazyStoredProperty.importer) //ここで初めてimporterにアクセスして初期化が行われる
値の変化を観察・応答する プロパティ・オブザーバ
レイジー以外のストアド・プロパティを対象に追加できます。
プロパティ・オブザーバを追加することで、値の変化前・変化後それぞれのタイミングで処理応答をすることができます。
具体的には、
willSet→値が格納される直前に呼び出されます。新しい値はデフォルトでnewValue
didSet→新しい値が格納された直後に呼び出されます。古い値はデフォルトでoldValue
class test{
var A:Int = 0 {
willSet(newValue){
print("値が\(newValue)に更新されます。")
}
didSet {
print("\(A)が新しい値で、\(oldValue)が古い値です。")
}
}
}
値を保有しない計算型 コンピューテッド・プロパティ
コンピューテッド(computed)は、「計算された」という意味です。
ゲッター/セッターを用いて、他のプロパティーの値から間接的に値を取得・設定する。
ゲッター・セッターについて簡単に記述すると、
- ゲッター
- 他のプロパティの値を元に処理をし、自身のプロパティの値を取得する(処理が単一の場合returnを省略できる)
- セッター
- 自身の新しい値(デフォルトでは、「newValue」)を元に処理をし、他のプロパティの値を更新する
基本的にゲッターは必須だが、セッターは任意です。
セッターがない場合は、読み取り専用コンピューテッド・プロパティと呼ばれます。
class computedProperty {
var name = "田中さん"
var circumstance = "田中さんはget待ちだ"
var GetSet: String {
get {
if name == "田中さん"{
return "田中さんをGetした"
}
set {
if newValue == "田中さんをGetした"{
circumstance = "「田中さんはGetされた」とsetされた"
}
}
複数のプロパティの処理を管理する プロパティ・ラッパー
プロパティラッパーは、一言でういとプロパティに関する処理を他の型に移譲するものです。
このプロパティラッパーをつかうことによって、個別の実装ケースに対してハードコーディングする必要がなくなります。
プロパティラッパーの使い方
- Property Wrapper にしたい型に
@propertyWrapper
をつける - Property Wrapper 型には
wrappedValue
というインスタンスプロパティを持つ - Property Wrapper を使用したい他の型のプロパティの頭に
@propertyWrapperの型名
をつける
プロパティラッパーを使った場合
import UIKit
var str = "Hello, playground"
@propertyWrapper
struct TwelveOrLess {
private var number: Int
init(){self.number = 0}
var wrappedValue:Int { //このwrappedValueが
get {return number}
set {number = min(newValue, 12)} // 上限値を12とする
}
}
struct SmallRectangle {
@TwelveOrLess var height: Int
@TwelveOrLess var weight: Int
}
var rectangle = SmallRectangle()
print(rectangle.height) // 出力:0
rectangle.height = 10
print(rectangle.height) //出力:10
rectangle.height = 14
print(rectangle.height) //出力:12
プロパティラッパーを使わなかった場合
import UIKit
var str = "Hello, playground"
struct TwelveOrLess {
private var number: Int
init(){self.number = 0}
var wrappedValue:Int {
get {return number}
set {number = min(newValue, 12)} // 上限値を12とする
}
}
struct SmallRectangle {
private var _height = TwelveOrLess()
private var _weight = TwelveOrLess()
var height: Int{
get {return _height.wrappedValue}
set {_height.wrappedValue = newValue}
}
var weight:Int {
get {return _weight.wrappedValue}
set {_weight.wrappedValue = newValue}
}
}
var rectangle = SmallRectangle()
print(rectangle.height) // 出力:0
rectangle.height = 10
print(rectangle.height) //出力:10
rectangle.height = 14
print(rectangle.height) //出力:12
このように、複数のプロパティに対して同様な処理を行いたい場合などでは、コードをすっきりさせることができます!
今回記事で触れた部分はごく一部に過ぎず、プロパティラッパーはまだまだ奥が深いです。
もっと詳しく勉強したい方はプロポーザルを見ることをお勧めします!
プロパティラッパーのプロポーザル
型自身に紐づく タイププロパティ(スタティックプロパティ・クラスプロパティ)
インスタンスプロパティ・スタティックプロパティ・クラスプロパティ
- インスタンスプロパティ:インスタンスのプロパティにアクセス
- スタティックプロパティ:
staticキーワード
を用いることで、その型自身とプロパティを紐付ける。サブクラスでオーバーライドは不可能 - クラスプロパティ:
classキーワード
を用いることで、クラス自身に紐付ける。サブクラスでオーバーライドが可能
struct staticProperty {
var name: String = "Aさん" // インスタンスプロパティ
static var name2:String = "Bさん" //スタティックプロパティ
}
let staticPropertyInstance = A()
print(staticPropertyInstance.name) //出力:Aさん
print(staticProperty.name) //エラー
print(staticPropertyInstance.name2) //エラー
print(staticProperty.name2) //出力:Bさん
class classProperty {
var name:String = "Cさん" //インスタンスプロパティ
class var name2:String = "ClassCさん" //クラスプロパティ
}
let classPropertyInstance = classProperty()
print(classPropertyInstance.name) //Cさん
print(classProperty.name) //エラー
print(print(classPropertyInstance.name2) //エラー
print(classPropertyInstance.name2) //ClassCさん