概要
今回は、Swiftの構造体・クラスについて復習したので、アウトプットしていきます。
構造体とは
構造体は値型の一種であり、ストアドプロパティの組み合わせによって1つの値を表すことができます。
簡単に言えば、継承不可のクラスのようなものであり、色々な種類の互いに関連するデータをまとめたものです。
また、以下のような特徴を持っています。
・辞書型とは違い、同じ構造のオブジェクトを複数生成可能
・クラスと違い参照不可
・クラスは参照型に対し、構造体は値型
・イニシャライザも関数も使用可能
定義方法
では早速、構造体の定義方法を見ていきましょう。
定義方法は以下のようにします。
struct 構造体名 {
構造体の定義名
}
定義方法だけを見ても分からないと思いますので、サンプルコードをいくつか見ていきましょう!
サンプルコード
次の例では、Personの情報を構造体で定義し、myPersonでインスタンス化をして、Personの情報を取り出しています。
struct Person {
var Name: String
var height: Int
var age: Int
}
// 構造体内のプロパティに名前、身長、年齢を定義し、インスタンス化している
var myPerson = Person(Name: "山田", height: 180
, age: 27)
// 上記のように値を定義すると、以下のような形で値を取り出せます
print("私の名前は\(myPerson.Name)です。 身長は\(myPerson.height)㎝で年齢は\(myPerson.age)です。")
// -実行結果-
// 私の名前は山田です。 身長は180㎝で年齢は27です。
次は、メソッドを使用していきましょう。
次の例では、構造体の中にメソッド(fullName)を定義しています。
struct Person {
var firstName: String
var lastName: String
func fullName() -> String {
return firstName + lastName
}
}
// 構造体内のプロパティに名前を定義します
var myPerson = Person(firstName: "山田", lastName: "太郎")
// 上記のように値を定義すると、以下のような形で値を取り出せます
print("氏名:\(myPerson.fullName())")
// -実行結果-
// 氏名:山田太郎
次は、init(イニシャライザ)を使用していきましょう。
イニシャライザは型のインスタンスを初期化します。全てのプロパティはインスタンス化の完了までに値が代入されていなければならないため、プロパティ定義時に初期化する必要があります。
次の例では、SimplePlus型に定義したinitを用いて、SimplePlus型のインスタンスを初期化しています。
struct SimplePlus {
var plus: Int
// 引数(構造体を使うときに指定された値をもらって、計算する)
init(num1: Int, num2: Int) {
// 計算する
plus = num1 + num2
}
}
// 以下のような形で値を取り出せます
var answer1 = SimplePlus(num1: 3, num2: 2)
print(answer1.plus)
// 実行結果
// 5
補足として、引数の型が違ったら複数書けます。
struct SimplePlus {
var plus: Int
init(num1: Int, num2: Int) {
plus = num1 + num2
}
// 引数の型が違ったら複数書ける
init(num1: String, num2: String) {
plus = Int(num1)! + Int(num2)!
}
}
// 以下のような形で値を取り出せます
var answer1 = SimplePlus(num1: 3, num2: 2)
print(answer1.plus)
var answer2 = SimplePlus(num1: "3", num2: "4")
print(answer2.plus)
// 実行結果
// 5
// 7
さらに、補足としてメソッド内のストアドプロパティの変更には、mutatingキーワードを用いることで変更できます。
struct Person {
var height: Int
var weight: Int
init(height: Int, weight: Int) {
self.height = height
self.weight = weight
}
mutating func someMethod() {
height = 180
weight = 60
}
}
var myPerson = Person(height: 150, weight: 60)
myPerson.someMethod()
myPerson.height // 180
myPerson.weight // 60
構造体のメソッド内のストアドプロパティの変更はコンパイラによってチェックされ、ストアドプロパティの変更を含んでいるのにもかかわらず、mutatingキーワードがついていない場合はエラーとなります。
struct Person {
var height: Int
var weight: Int
init(height: Int, weight: Int) {
self.height = height
self.weight = weight
}
// mutatingキーワードが付いていない場合はコンパイルエラーとなる
func someMethod() {
height = 180
weight = 60
}
}
構造体とクラスの違い
##クラスとは?
クラスは構造体と同様の構造を持つ型です。構造体との大きな違いは2つあり、一つは参照型であること、もう一つは継承が可能であることです。
それでは、定義方法を見ていきましょう。
クラスの定義は以下の通りです。
class クラス名 {
クラスの定義
}
先程と同様に、定義方法だけを見ても分からないと思いますので、早速サンプルコードをいくつか見ていきましょう!
##サンプルコード
次の例では、HamburgerクラスにメソッドsayNakamiを定義しています。
class Hamburger {
var nakami = "パティ"
func sayNakami () {
print("中身は"+nakami+"です")
}
}
var hamburger = Hamburger()
hamburger.sayNakami()
// 実行結果
// 中身はパティです
ここまでは、構造体とあまり違いがないように見えますね。
では、次は継承の例をみていきましょう!
次の例では、HamburgerクラスがJunkFoodクラスを継承しています。
HamburgerクラスはJunkFoodクラスを継承しているので、JunkFoodクラスで定義されているnakami、junkFoodプロパティやdetailJunkFood()メソッドが自動的に利用可能になります。
class JunkFood {
let nakami: String
var junkFood: String {
return "ハンバーガー"
}
init(nakami: String) {
self.nakami = nakami
}
func detailJunkFood() {
print("junkFood: \(junkFood)")
print("nakami: \(nakami)")
}
}
class Hamburger: JunkFood {
let count: Int
init(nakami: String, count: Int) {
self.count = count
super.init(nakami: nakami)
}
}
let hamburger = Hamburger(nakami: "チーズ", count: 2)
print("\(hamburger.nakami)","\(hamburger.count)枚")
hamburger.detailJunkFood()
// 実行結果
// チーズ 2枚
// junkFood: ハンバーガー
// nakami: チーズ
##構造体とクラスの違いを深堀する
最後に、構造体とクラスを定義して、どのような挙動を示すか見ていきましょう!
次の例では、構造体は、colorStruct2.colorを変更しても、colorStruct1は変更しない。クラスの場合は、colorClass2.colorを変更したら、colorClass1も変更されるという挙動を示しています。
// 構造体(値型)
struct ColorStruct {
var color:String
}
// クラス(参照型)
class ColorClass {
var color:String
init(color:String) {
self.color = color
}
}
var colorStruct1 = ColorStruct(color: "青")
var colorStruct2 = colorStruct1
colorStruct2.color = "黒"
print("colorStruct1:\(colorStruct1.color)、colorStruct2:\(colorStruct2.color)")
// 出力結果
// colorStruct1:青、colorStruct2:黒
var colorClass1 = ColorClass(color: "青")
var colorClass2 = colorClass1
colorClass2.color = "黒"
print("colorClass1:\(colorClass1.color)、colorClass2:\(colorClass2.color)")
// 出力結果
// colorClass1:黒、colorClass2:黒
まとめ
ここまで、長々と構造体とクラスについて述べてきましたが、結局、どういう時にどっちを使えばいいか迷ってしまいますよね。
実は、その問題はApple公式の見解が発表されています。詳しく知りたい方は、是非ご覧ください!
https://developer.apple.com/documentation/swift/choosing_between_structures_and_classes
結論を言うと。基本的にはAppleは構造体を推奨しています。
Appleの見解によると、以下の3つの場合の時はクラスを使用した方が良さそうですね。
・Objective-CのAPIを使ったり、そのAPIを含む標準フレームワークのクラス(UIViewControllerなど)を継承したい場合
・モデル化したデータをオブジェクト間で統一的に変更(参照渡しが適切)しやすくしたい場合
・データが大きい場合
上記の内容を知っておくだけでも、構造体とクラスの使い分けの判断が容易になりそうですね!
--
今回は、Swiftの構造体とクラスについて復習し、まとめてみました。
構造体とクラスはどっちを使えばいいんだろうと悩みがちなので、アウトプットすることによって頭の中を整理することができたので良かったです。
今後、コードを書くときは構造体が使えるところはないか常に意識していきたいですね。
参考文献
・Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語
・Swiftにおける構造体(struct)vsクラス(class)問題に向き合ってみた
・Choosing Between Structures and Classes