【Swift】構造体の使い方。複数データを1つの変数で管理する入れ物。(Swift 2.1、XCode 7.2)
構造体とは
構造体とは、配列や辞書と同じように複数のデータを1つの変数で管理できる入れ物である。関数を定義することができるので、管理しているデータを使って用途に応じた様々な処理を実装することができる。
構造体は以下のように定義する。
1 2 3 4 5 6 7 8 |
struct 構造体名 { var 変数名: データ型 var 変数名: データ型 ... func 関数名(引数) -> 戻り値のデータ型 { 処理 } } |
構造体と辞書の使い分け
辞書を使うより構造体を使った方が良い例を見てみよう。
例えば、重さ「10kg」、産地「茨城」、通常価格「3000円」、賞味期限(商品によって異なる)の米袋3個を変数で作る場合について考える。賞味期限は変数を作ったあとに設定するものとする。
辞書を使う場合は以下のようなコードになる。商品の変数を作るたびに重さ、産地、通常価格を設定しなければならないのが冗長だ。変数をコピーして作ればいいという指摘があるかも知れないが置いておく。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* * 辞書を使って商品情報の変数を作る例。 */ //格納する値を定数で準備 var weight_value:Int = 10 let product_value = "茨城" let price_value = 3000 //辞書を利用して米袋の変数を作る。 var rice1:Dictionary<String,AnyObject> = ["weight":weight_value,, "product":product_value, "price":price_value] var rice2:Dictionary<String,AnyObject> = ["weight":weight_value, "product":product_value, "price":price_value] var rice3:Dictionary<String,AnyObject> = ["weight":weight_value, "product":product_value, "price":price_value] //賞味期限を設定する。 rice1["expiration date"] = "20160503" rice2["expiration date"] = "20160811" rice3["expiration date"] = "20161029" |
構造体を使うと以下のようになる。Rice()と書くだけで重さ、産地、通常価格が設定された変数を作ることができた。このように、データの詰め合わせ内容があらかじめ決まっているようなときは構造体を使うのが便利である。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/* * 構造体を使って商品情報の変数を作る。 */ //構造体を定義 struct Rice { var weight = 10 var product = "茨城" var price = 3000 var expiration_date:String? } //構造体を利用して米袋の変数を作る。 var rice1 = Rice() var rice2 = Rice() var rice3 = Rice() //賞味期限を設定する。 rice1.expiration_date = "20160503" rice2.expiration_date = "20160811" rice2.expiration_date = "20161029" |
構造体に関数を持たせる
構造体の中にイニシャライザ(初期化メソッド)を定義すれば、構造体が呼び出されたときの初期化処理を実装することができる。
例えば、上記コードでは変数を作ったあとに賞味期限を設定したが、下記コードのようにイニシャライザを定義しておくと、変数を作るときの引数で賞味期限を設定できるようになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/* * 構造体にイニシャライザを定義 */ //構造体 struct Rice { var weight = 10 var product = "茨城" var price = 3000 var expiration_date:String? //イニシャライザ init(expiration_date:String){ self.expiration_date = expiration_date } } //米袋の変数を作る。 var rice1 = Rice(expiration_date:"20160503") print(rice1) //実行結果 //Rice(weight: 10, product: "茨城", price: 3000, expiration_date: Optional("20160503")) |
ちなみに以下のコードのように、構造体を呼び出すときにすべてのプロパティの値を引数に与えれば、イニシャライザを定義していなくても変数を設定できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//構造体 struct Rice { var weight = 10 var product = "茨城" var price = 3000 var expiration_date:String? } //米袋の変数を作る。 var rice1 = Rice(weight:10, product:"茨城", price:3000, expiration_date:"20160503") print(rice1) //実行結果 //Rice(weight: 10, product: "茨城", price: 3000, expiration_date: Optional("20160503")) |
以下のコードは、構造体の中に賞味期限のチェックをする関数を定義した例。このような格納データを使ったチェック処理や頻繁に利用する計算は構造体の中に関数として定義しておくと便利だ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
//構造体を定義 struct Rice { var weight = 10 var product = "茨城" var price = 3000 var expiration_date:String = "19700101" //賞味期限チェック関数 func expiredCheck() -> Bool { let dateFormatter = NSDateFormatter() dateFormatter.locale = NSLocale(localeIdentifier: "en_US") dateFormatter.dateFormat = "yyyyMMdd" if( Int(expiration_date)! - Int(dateFormatter.stringFromDate(NSDate()))! > 0 ) { //今日の日付が賞味期限よりあとなら期限切れ return true } else { return false } } } //米袋の変数を作る。 var rice1 = Rice() rice1.expiration_date = "20160503" //賞味期限切れチェック print(rice1.expiredCheck()) //実行結果 //true |
構造体とクラスの使い分け
構造体の仕様はクラスと良く似ているので、構造体とクラスの違いと使い分けについて疑問を持つ人が多い。大きな違いは「構造体は値型、クラスは参照型」ということである。
値型は、変数の保存先にデータそのものを保持し、別の変数に代入するときは値のコピーが作られる仕様である。例えば、下図の変数Aを変数Bに代入した場合、変数Aでweightの値を20に変更したあとに、変数Bのweightを確認しても10のままになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/* * 値型のコピーの例 */ //構造体 struct TestClass { var weight = 10 var price = 3000 } var A = TestClass() //変数Aを作成 var B = A //変数Bに変数Aを代入 A.weight = 20 //変数Aの値を変更 print(A.weight) print(B.weight) //実行結果 //20 //10 |
一方、参照型は変数の保存先にデータの参照先を保持し、別の変数に代入するときは参照先のコピーが作られる仕様である。変数A、変数Bは同じデータを見ているので、例えば、変数Aでweightの値を20に変更したあとに、変数Bのweightを確認すると20になる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/* * 参照型の変数のコピーの例 */ //クラス定義 class TestClass { var weight = 10 var price = 3000 } var A = TestClass() //変数Aを作成 var B = A //変数Bに変数Aを代入 A.weight = 20 //変数Aの値を変更 print(A.weight) print(B.weight) //実行結果 //20 //20 |
クラスは参照型だが、自身をコピーして返す関数を持たせることで値型のような振るまいをさせることができるので、どちらを使うか迷うときはクラスを使えば良い。
そうなると構造体の存在意義が微妙になるが、設計者の視点で考えると、クラスを設計するときは新たな設計書、テスト仕様書を作ることになるので、構造体を設計するよりも大掛かりになるイメージがある。
一方、構造体はクラス内の1つの変数として設計するので軽いイメージがある。なので、参照型にする必要が無いのであれば、まとめる変数の数が少ないときは構造体を使ったほうがレビューする人にとっても有難いことになる。