Structはどう使えばいいのか
Appleは、できるだけStructを使うように推奨しているらしい。
Classじゃなくて、Structで書きたいな、と思ったのでサクッと確認するため基礎文法の確認。
※参考
the swift programming language swift 4.2 Structure and Classes
実行環境
Xcode:10.1
Swift:4.2.1
そもそもStructとは
オブジェクトの一種。なので使うときは、インスタンス化が必要。
classが参照型なのに対して、Structは値型。つまり、classのようにオブジェクトそのものを参照するのでなく、そのコピーを参照する。よって、インスタンス化されたものを別々の変数にいれた場合、それぞれは別々に独立したコピーになる。これがclassだと1つのオブジェクトを共有することになるので、例えばインスタンス化されたオブジェクトのひとつの要素を書き換えると、別の変数でもそれが共有されることになる。
そして、classのように継承はできない。
こんな風に使う
Structを定義する
例1:nilを許容しない宣言
1 2 3 4 5 |
struct User { var id:Int var userName:String var weight:Double } |
初期値を入れていないため、この場合はインスタンス化する際に、次のように初期値を代入することが求められる。ちなみに、初期値の代入を忘れてもXcodeが叱ってくれるようになっている。
1 2 |
let Tom = User(id: 2, userName: "Tom", weight: 70.0) print("Tomのidは、\(Tom.id)です") //Tomのidは、2です |
なお、Structは配列の要素として突っ込むことも可能となっている。
1 2 3 4 5 6 7 8 9 |
let Tom = User(id: 2, userName: "Tom", weight: 70.0) let John = User(id: 3, userName: "John", weight: 65.0) let array:[User] = [Tom,John] print("arrayの中身は、\(array)です") //arrayの中身は、[Trial_Struct.User(id: 2, userName: "Tom", weight: 70.0), Trial_Struct.User(id: 3, userName: "John", weight: 65.0)]です ※Trial_Strcutというのはプロジェクトファイル名 print("arrayの最初に入っている要素の、idは\(array[0].id)です") //arrayの最初に入っている要素の、idは2です |
例2:nilを許容する宣言
1 2 3 4 5 |
struct User { var id:Int? var userName:String? var weight:Double? } |
例3:初期値を予め入れて宣言
1 2 3 4 5 |
struct User { var id:Int = 1 var userName:String = "太郎" var weight:Double? = 60.5 } |
例4:イニシャライザを定義して宣言
1 2 3 4 5 6 7 8 9 10 11 12 13 |
struct User { var id:Int var userName:String var weight:Double init(number:Int,name:String,decimal:Double) { self.id = number self.userName = name self.weight = decimal //上記の値によって、特定の処理を実行 print("ユーザーのidは\(id)で、名前は\(userName)で、体重は\(weight)です") } } |
クラスと同様に、イニシャライザを使うことで、イニシャライズ時に一定の処理を付け加えることができる。
宣言するときに初期値を与える方が使いやすそう
とりあえず、例1のような形で実装するのが便利かと思いきや、意外と面倒なこともある。
例えば、ユーザー情報を入力してもらうときに、UILabelのtextを置いておくとする。
そして、それらをStructをインスタンス化したものに代入するような実装をするとnilチェックの問題から、??や!が多い実装になってしまい、見づらくなってしまう。
if let でnilチェックをクリアしているはずなのに何故かXcodeが警告を出してくる。
これは見づらくなりそうで嫌。
1 2 3 4 5 6 |
if let id = idLabel.text,let userName = userNameLabel.text,let weight = weightLabel.text{ //id,userName,weightの全てがnilではないとき(けれども、Int?とDouble?はアンラップするようにXcodeがが怒ってくる var a = User(id: Int(id), userName: userName, weight: Double(weight)) }else{ // id,userName,weightのいずれかがnilのとき } |
と思って例1の方法をやめて、例3のように予め初期値を設定して定義して同じことを試してみるも、やはり同じく型変換の際にアンラップするよう求められるので、Structの定義の仕方うんぬんの話ではなさそう。
おとなしく!か??をつけてアンラップする。ただそうした場合、上記のようにUser(……)と()の中だけで色々設定してしまうと見づらくなるので、やはり
1 2 3 4 |
var a = User() a.id = Int(idLabel.text!)! a.userName = userNameLabel.text! a.weight = Double(weightLabel.text!)! |
とするか
1 2 3 4 |
var a = User() a.id = Int(idLabel.text ?? "") ?? 0 a.userName = userNameLabel.text ?? "" a.weight = Double(weightLabel.text ?? "") ?? 0.0 |
のようにした方が見やすそう。こう考えると、結局のところ、まずはじめにStructを定義するときは初期値を与えた方が良さそう。呼び出しの際に適当な値で初期化してから始めることもできるが、それはそれで面倒なので。
なお、Structはその中でファンクションを定義することもできる。このあたりはまた今度行いたい。