見出し画像

【Swift】Structureでハマったとこ

こんにちは!Ninomaeです
今回は、学習の中で出会った覚えておきたい事柄たちをまとめる回になります。
プログラミングの専門的なことなんか読みたくねぇ!って人は、「スキ」を押していただいてから、ブラウザバック推奨です(笑)

Structure(構造体)に入る前に

プログラミングではデータの取扱が重要になります。例えば「3」という数字1つとっても、3が文字列("3")なのか、整数(3)なのか、小数点(3.0)なのかといったように複数のパターンが考えられます。

この文字列や整数、小数点などのことをデータ型といい、プログラムの世界ではデータ型に対してめちゃくちゃストイックになる必要があります(そうじゃない言語もある)。なので、データ型が違う値同士(整数の3と小数の3)ではエラーが出てしまい、プログラムは動きません。

このように、プログラミングではデータの扱い方が重要です。では、予め決められているデータ型しか使えないのでしょうか?また、複数のデータ型をまとめた変数や定数を作ることはできないのでしょうか?

そこで、Structures(構造体)の出番です。

Structures(構造体)とは

プログラムやアプリの特定のニーズに合う独自のデータ型を定義することができる機能。例えばPersonという名前のデータ型を作りたければ以下のようにします。

struct Person {
    var name: String
}

nameという変数(プロパティ)を持ったPerson型の完成です!
これを使うためには、Person型のインスタンスを作成しなければなりません。(いすの設計図をもとに、いすを作成するみたいなイメージ)

let firstPerson = Person(name: "Jasmine")
print(firstPerson.name)

これで、Person型のインスタンスを作成することができました。1行目では、nameプロパティに"Jasmine"という名前を持ったPerson型のデータをfirstPersonに代入するという意味になります。
Person型のプロパティのデータを参照したいときは、変数名.プロパティ名という形で参照することができます。

構造体には、変数(プロパティ)だけではなく、関数(メソッド)も定義することができます。

struct Person {
    var name: String
    func sayHello() {
        print("Hello, there! My name is \(name)!")
    }
}
let person = Person(name: "Jasmine")
person.sayHello()

// --- Console Output ---
// Hello, there! My name is Jasmine!

上のようにfuncキーワードを使って、関数と同様にメソッドは定義できます。その構造体の中にある変数の埋め込みもできていますね!
メソッドの実行は、プロパティの参照と同様に、変数名.メソッド名で可能です。

ここまでが構造体の基本的な使い方です。
本当はもう少し、丁寧に書こうと思いましたが、疲れたのでそのまま自分の本題に入ります(笑)

Mutating Method

Mutating Methodはインスタンスメソッド内で構造体のプロパティを変更するときに使います。
mutating キーワードをfuncの前につければヨシ!って感じです

struct Odometer {
	var count: Int = 0 // Assigns a default value to the 'count' property.
	mutating func increment() {
		count += 1
	}
	mutating func increment(by amount: Int) {
		count += amount
	}
	mutating func reset() {
		count = 0
	}
}
var odometer = Odometer() // odometer.count defaults to 0
odometer.increment() // odometer.count is incremented to 1
odometer.increment(by: 15) // odometer.count is incremented to 16
odometer.reset() // odometer.count is reset to 0

Computed Properties

計算値を返すロジックをプロパティで実行できる機能のことです。
以下では、絶対温度、摂氏温度、華氏温度の3つのプロパティを保有するTemperature構造体を例にとって説明します。

struct Temperature {
	var celsius: Double
	var fahrenheit: Double
	var kelvin: Double
}
let temperature = Temperature(celsius: 0, fahrenheit: 32.0, kelvin: 273.15)
// 3種類の温度に対して、それぞれユーザーが計算して、ベタ打ちしてinitするのはだるい

上記のコードをイニシャライザを使って計算する方法

struct Temperature {
	var celsius: Double
	var fahrenheit: Double
	var kelvin: Double

	init(celsius: Double) {
		self.celsius = celsius
		fahrenheit = (celsius - 32) / 1.8
		kelvin = celsius + 273.15
	}

	init(fahrenheit: Double) {
		self.fahrenheit = fahrenheit
		celsius = (fahrenheit - 32) / 1.8
		kelvin = celsius + 273.15
	}

	init(kelvin: Double) {
		self.kelvin = kelvin
		celsius = kelvin - 273.15
		fahrenheit = celsius * 1.8 + 32
	}
}

let currentTemperature = Temperature(celsius: 18.5)
let boiling = Temperature(fahrenheit: 212.0)
let freezing = Temperature(kelvin: 273.15)

// 複数のイニシャライザを使うことで、多くのstateや情報を包含することができる。
// だけど、もし温度が変わったら、3つすべてのプロパティを手作業でアップデート
// しないといけない → エラーのもとになるし、めんどくさい

これは、​コンピューテッドプロパティを使ったやり方で解決できる!!!

struct Temperature {
	var celsius: Double

	var fahrenheit: Double {
		celsius * 1.8 + 32
	}
	
	var kelvin: Double {
		celsius + 273.15
	}
}

let currentTemperature = Temperature(celsius: 0.0)
print(currentTemperature.fahrenheit)
print(currentTemperature.kelvin)

// ----- Output -----
// 32.0
// 273.15

コンピューテッドプロパティに定義されているロジックは、プロパティにアクセスされるたびに実行されるので、返ってくる値も自動的にアップデートされるものになっています。

Property Observers

任意のプロパティの値を監視し、プロパティの値の変化に対応することができる機能のことです。
プロパティオブザーバーは、新しい値がプロパティの現在の値と同じであっても、プロパティが設定されるたびに呼び出されます。
任意のプロパティに定義できるオブザーバー・クロージャー(コードのブロック)は、`willSet`と`didSet`の2つです。

struct StepCounter {
	var totalStep: Int = 0 {
		willSet {
			print("About to set totalSteps to \(newValue)")
		}
		didSet {
			if totalStep > oldValue {
				print("Added \(totalSteps - oldValue) steps")
			}
		}
}

var stepCounter = StepCounter()
stepCounter.totalSteps = 40
stepCounter.totalSteps = 100

// -----Output-----
// About to set totalSteps to 40
// Added 40 steps
// About to set totalSteps to 100
// Added 60 steps

totalStepが変更されるときはいつでも、willSetがはじめに更新されて、totalStepに設定される新しいプロパティ値にアクセスできます。
新しいプロパティ値の名前はnewValueで参照できます。

次に、プロパティ値がアップデートされると、didSetが呼び出されます。そして、前のプロパティ値にはoldValueという名前でアクセスできます。

Type Properties And Methods

ここまで書いてきたものは、型の個々のインスタンスに関するプロパティやメソッドです。
しかし、Swiftでは型自体に対して、プロパティやメソッドを追加することができます。

型にプロパティやメソッドを追加するには、staticキーワードを使います。
型のプロパティはプロパティが型に関連するけど、インスタンス自体の特性ではないときに便利です。

struct Temperature {
	static var boilingPoint = 100
}

終わりに

かなり駆け足になりましたが、構造体で自分がハマったところはうまくまとめられたのではないかと思います。

気が向いたら追記して、もう少し網羅的な構造体の紹介記事にしようかなと考えています!
ここまでお付き合いくださり、ありがとうございました!

では、また✋

この記事が気に入ったらサポートをしてみませんか?