なえT備忘録

何の参考にもならないかもしれませんが、いつかは参考になるようにします

Swift サルでもわかる構造体 VS まだ構造体がわからないサル (値渡しと参照渡し、イニシャライザ)

どうもサルです。

 

私サルめは、構造体のことがわかったようでわからないまま、XcodeをシバいてStoryboardをいじいじしたりDelegateで通知をとばしたりViewControllerをいじいじしたりUserDefaultにデータをいれたりしていました。

 

流石にこのままではまずいと、頭のヘルメットが黄色に光り輝き始めたため、そろそろきちんと整理しようかと思い立った所存です。

 

softmoco.com

 

こちらのサイトを参考にしました

>>>Swift の struct(構造体)では、値を保持するためのプロパティや関連する機能のメソッドなどをまとめて定義しておいて、必要な時にその struct のインスタンスを生成して使うことができます。

 

使い方としてはクラスと同じと言うことですね

 

書き方の例 >>>>>>>>>>>>>>

 

struct Fueltank {

            var alert: String = "A"

            var fuel: Int

            

            init(alert: String, fuel: Int){

                self.alert = alert

                self.fuel = fuel

            }

            

            func indication(){

                print("\(alert) is \(fuel)L")

            }

        }

 

        var ind = Fueltank(alert: "Fuel Remaining Amount", fuel: 30)

        ind.indication()

 ⇨結果 Fuel Remaining Amount is 30

 

クラスとの差異としては、①継承ができない ②参照渡しでなく値渡し ③デイニシャライザが使えない の3点。

 

①継承ができない については、新しくクラス、構造体を作成する際、その参照元として(スーパークラスのように)継承をおこなうことができない。クラスの継承については以前の記事で説明済み

 

②参照渡しでなく値渡しとなる とは、下記例で見比べるとわかりやすい。

 

構造体で実行した場合(値渡し)>>>>>>>>>>>>>>

struct Fueltank {

    var alert: String = "A"

    var fuel: Int

    

    init(alert: String, fuel: Int){

        self.alert = alert

        self.fuel = fuel

    }

    

    func indication(){

        print("\(alert) is \(fuel)L")

    }

}

 

var ind = Fueltank(alert: "Fuel Remaining Amount", fuel: 30)

var ind2 = ind

ind2.fuel = 15

 

ind.indication()

ind2.indication()

 ⇨結果 Fuel Remaining Amount is 30L

                   Fuel Remaining Amount is 15L

 

 

クラスで実行した場合(参照渡し)>>>>>>>>>>>>>>

class Fueltank {

    var alert: String = "A"

    var fuel: Int

    

    init(alert: String, fuel: Int){

        self.alert = alert

        self.fuel = fuel

    }

    

    func indication(){

        print("\(alert) is \(fuel)L")

    }

}

 

var ind = Fueltank(alert: "Fuel Remaining Amount", fuel: 30)

var ind2 = ind

ind2.fuel = 15

 

ind.indication()

ind2.indication()

 ⇨結果 Fuel Remaining Amount is 15L

                   Fuel Remaining Amount is 15L

 

値渡しの場合は、indと代入後にfuelプロパティを書き換えたind2の値が異なるが、

参照渡しの場合、両方とも同じ値となる。

 

これは、値渡しが通常の演算通り、代入時の値(30)を渡しているのに対し、

参照渡しは、値(30)のプロパティはここにありますよ、という情報を渡している。

値渡しされた変数(ind2)を書き換えても、代入元(ind)の値はかわらないが、

参照渡しされた変数(ind2)を書き換えると、参照元となるインスタンスのプロパティそのものを書き換えてしまい、代入元(ind)も同じプロパティを参照しているので、両者とも値が変わってしまう。

 

例えるなら、Webページ上にある画像に対して、A君がB君にコピーして渡すのが値渡し

B君が画像にどんなコラージュをあててもA君の画像はもとのまま

 

A君の画像

 

B君の画像

 

Webページ上にある画像に対して、A君がB君にページリンクを渡すのが参照渡し

B君がリンク元の画像にコラージュをあてると、同じリンクから参照しているA君の画像もコラ画像になってしまう。

 

A君の画像

B君の画像

 

要は値を渡すか参照元を渡すかって言う、まんまの意味なんですけどね。

ちなみに値渡しのところで、意図的に参照渡しを行いたい場合は、引数の前に&をつかえば実行可能。詳しくは下記ページをご参照ください。

qiita.com

 

 

 

 

③デイニシャライザが使えないとは?

そもそもイニシャライザとは?

qiita.com

 

人のQiita記事で学び、自分のはてブでアウトプットするサル

 

イニシャラザの中身は、クラス、構造体がインスタンス化される際の、すべてのプロパティ、メソッドに対する初期値 ※基本的にすべて初期値が存在しなければならない。

UIKit Frameworkに最初から入っているスーパークラスなどではなく、自分でクラス、構造体を作成する場合、すべてのプロパティ、メソッドに初期値を設定する必要がある。

 

なぜ初期値が存在しないといけないかと言うと、クラス、構造体のプロパティ、メソッドに初期値がなければ、インスタンス化した際の初期値がない⇨nilとして処理され、型の不一致、および解放前メモリへの不正アクセス等でエラーが発生するため。(nilはオプショナル型、Any型以外では許容されていないよ!なのでこれらに限り初期値はなくてもOK)

 

値渡し、参照渡しの説明に使ったコードだと、こちらがイニシャライザ部分

ここでFueltank内の各プロパティの初期値を設定している。

※selfと記載することで、このクラス/構造体自身のメソッド、プロパティに代入する、という意味となる

    init(alert: String, fuel: Int){

        self.alert = alert

        self.fuel = fuel

    }

 

その後、インスタンス化したFueltankクラスの引数として、alertとfuelに引数を渡しているが、これはイニシャライザで定義した初期値を上書きしている、という事。

var ind = Fueltank(alert: "Fuel Remaining Amount", fuel: 30)

 

このイニシャライザ、クラスの場合は毎回必ず記述しないといけないが、構造体の場合はイニシャライザを定義しない場合、暗黙的に自動で全てのメソッド、プロパティに初期値が付与される。これを全項目イニシャライザとよぶ。

 

●車の構造体 イニシャライザなしでもインスタンス化可能

struct Car {

    let model: String

    let type: String

    let price: Double

    let serial: Int

}

実際には、下記のイニシャライザが自動で作成され、かつ省略されている。

init(model: String, type: String, price: Double, serial: Int){

    self.model = model

    self.type = type

    self.price = price

    self.serial = serial

}

 

基本的にイニシャライザは、インスタンス生成された際に一番最初に読み込まれる情報だが、逆にインスタンスが破棄される際(ある意味一番最後)に読み込まれるデイニシャライザ という構文もある。これは基本的に暗黙的に処理される内容だが、自分で内容を書くことも可能。

struct Car {

    let model: String

    let type: String

    let price: Double

    let serial: Int

 init(model: String, type: String, price: Double, serial: Int){  //instance生成時呼出 

      self.model = model

      self.type = type

      self.price = price

      self.serial = serial

    }

     deinit{                                                                                      //instance終了時呼出

     print("Car instance is terminated")

    }

}

 

デイニシャライザはクラスのみ実行可能で、構造体では実行不可能。この違いがある

 

 

以上3点の差異について説明できました。書き方、使い方はクラスみたいなもんで、クラスは何らかの機能を有していて、インスタンス生成してクラス自体がもつ目的を遂行するのに対し、構造体は単なるデータ、処理をまとめて保持する倉庫みたいな認識でいいのかな?

 

また一つ賢くなったサルでした

 

 

 

youtu.be

 

山田亮一Forever