はじめに
コードを見てた際に、「あれ??なんだこのプロパティget、setがあったり、{}の中でちょっとした処理が書いてある???」となりました。
他の変数を参照していたりしてなんかよーわからないなーって思っていました。
どうやら計算プロパティというものらしく処色々と調べてみました。
ざっと概要を掴みたい自分のような初学者向けの方の助力になれば嬉しいです!
計算プロパティとは?
まずは、Documentaを見てみましょう。
【原文】
Computed Properties
In addition to stored properties, classes, structures, and enumerations can define computed properties, which don’t actually store a value. Instead, they provide a getter and an optional setter to retrieve and set other properties and values indirectly.
(https://docs.swift.org/swift-book/documentation/the-swift-programming-language/properties/)
【翻訳】
計算型プロパティ(Computed Properties)
格納プロパティに加えて、クラス・構造体・列挙体は計算型プロパティを定義できます。計算型プロパティは実際の値を保存せず、代わりに getter と(必要に応じて)setter を提供して、他のプロパティや値を間接的に取得・設定します。
計算型プロパティをまとめると
- 固定の値を持たずリクエストごとに計算した値を返すプロパティ
- get,setとかを使って他プロパティを参照したり・設定することができる
ということです。
固定の値を持たないことが大きいですね。
1. デフォルト宣言
変数名をつけて、「:」の後に返却する型を明示します。
{}の中に処理を記述できます。
読み取り専用のプロパティになります。
var プロパティ名: 返却型 {
処理
}
2. Get・Set宣言
GetとSetを明示的に宣言することが可能です。
var プロパティ名: 返却型 {
get { 取得する値の処理 }
set { 値を設定する処理 }
}
Get
get { 取得する値の処理 }
getを明示する方法です。
省略した場合には1. デフォルト宣言と同じになります。
何らかの値を取得するための処理を記述します。
他のプロパティの値を複数参照することも可能です。
Set
set(value) { 値を設定する処理 }
Setで他プロパティへ値を設定することが可能です。
valueを引数として渡していますが、valueは省略可能です。
省略した場合にはnewValueというヘンスが自動的に渡されます。
コード例
Get,Setを使用したファイルの情報を構造体で管理する算出プロパティのコード例を載せます。
import SwiftUI
struct FileContent {
/// ファイルのルートパス
let fileRoot: String
/// ファイル名(拡張子なし)
var fileName: String
/// ファイルの拡張子
let fileExtension: String
/// 計算プロパティ:ファイルのフルパス
var filePath: String {
get { "\(fileRoot)/\(fileName).\(fileExtension)" }
set { fileName = newValue }
}
}
struct ContentView: View {
@State private var fileContent = FileContent(
fileRoot: "/Users/root/Documents",
fileName: "sample",
fileExtension: "txt"
)
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("ファイルパス")
.font(.headline)
Text(fileContent.filePath)
.font(.system(.body, design: .monospaced))
.textSelection(.enabled)
}
.padding()
.onAppear {
// 例:ファイル名を更新(拡張子/ルートはそのまま)
fileContent.filePath = "updatedFileName"
}
}
}
コードの説明
FileContentという構造体を宣言して、ファイルに関しての情報を管理しています。
構造体の中にあるfilePathが計算型プロパティです。
Getで他のプロパティを用いてファイルのフルパスを取得
Setで他プロパティのファイル名に値を設定しています。(明示的に引数を指定していないのでnewValueが渡されます)
Viewの中ではFileContentの構造体をインスタンス化
.onAppearモディファイアで計算型プロパティのfilePathのsetを使用してfileNameの変更をしています。
TextにfilePathのGetを用いてファイルのフルパスを表示させています。
メソッドとの違い使い分け
さてここまで計算型プロパティの概要だったり、使い方を紹介しました。
なんかメソッド見たいですよね。
計算するし、他のプロパティに対して値を設定するしで区別が曖昧になりそうです。
使い分けに関して公式に記載がありました。
【原文】
Document the complexity of any computed property that is not O(1). People often assume that property access involves no significant computation, because they have stored properties as a mental model. Be sure to alert them when that assumption may be violated.
【翻訳】
O(1) でない算出(計算)プロパティには、必ず計算量を明記すること。
多くの人は、格納プロパティを前提に「プロパティアクセスに大きな計算は発生しない」と考えがちである。
その前提が成り立たない場合は、必ずその旨を利用者に注意喚起すること。
つまり計算が複雑になったり処理が重たいものは計算プロパティで扱わないようにしましょうとのことです。
算出型プロパティは他プロパティを参照する程度に限定した方が良いと思います。
(そもそもプロパティは状態を表すと思っているので、個人的にプロパティの中で計算とかさせるとモヤっとします。。。)
注意点
さて、ここから一点Setに関して注意点です。というか、筆者が注意した方が良いなと思う点です。
Setは他プロパティへの値が設定できて便利ですが意図しない変更が行われる可能性があります。
上で載せたコードの以下の部分を見てください。
.onAppear {
// 例:ファイル名を更新(拡張子/ルートはそのまま)
fileContent.filePath = "updatedFileName"
}
filePathに新規のファイル名を代入してfileNameプロパティを更新していますがパッと見ただけでは何を更新しているかが、わからなくないですか?
更新内容を見るのに結局宣言もとを見る必要があり可読性的に微妙ですよね。
筆者的には便利じゃーん〜って思っていたのですが、よくよく考えてみると自分の首を絞める場合があるとわかりました。
プロパティ名をもっと上手くやったら可読の負荷が下がりそうですが導入は慎重に考えて使った方がいいですね。