見出し画像

【じっくりSw1ftUI 25】Essentials第12章〜Playgroundで遊ぼう11〜構造体と列挙型。ここはクラスとの違いを理解しとけば十分。実践編で嫌ってくらいやるからね、、、💦

さてと、前回は、

で一旦、中断して、しっかり書きたいことは書いてるから、今回は、

早速、本編に〜〜〜!

オイラがやってる手法は一貫して、本来の

テスト駆動開発

に則っているつもりだから、このマガジンと

でも全部、やってることは同じで。

コードを書く→すぐに動かして検証する。
👉小さく作って、すぐにバグを潰し、大きく動かす。

ま、最近は、テスト駆動開発自体、JSTQBとかで縦割りに細分化されてマニアックになってるから、上澄みしか知らない人は、テスト駆動開発とは〜〜って人も多いけどね👀💦

じゃ、前置きはこれくらいにして、

*以降のオイラの学びの記録なんざどーでも良いって人は、

でも見てね〜〜〜

第12章をじっくり読んでく〜〜〜!

まずは、前回まででやった

クラスと構造体は非常に似通ってる👀

ってことが書いてあんね。

だから、今から違いを主に紹介するぜ

ってゆーてる👀💦

構造体とは、

  1. (クラスと同じく、)オブジェクト志向言語に根差したモノ

  2. カプセル化と再利用が出来るのもクラスと同じ

  3. プロパティ、メソッド、イニシャライザなんかも使える

構文(てか、サンプル)

struct M_KakuSampleStruct12_1{
    var name: String
    init(name: String) {
        self.name = name
    }
    func msgM_KakuName(){
        "オイラは、" + name + "だぞ!"
    }
}

コイツをクラスで宣言してみると、、、

class M_KakuSampleClass12_2{
    var name: String
    init(name: String) {
        self.name = name
    }
    func msgM_KakuName(){
        "オイラは、" + name + "だぞ!"
    }
}

で今んところはっきし言って、最初を

structて書くかclassって書くか=宣言の仕方が違うだけ〜〜〜

って感じがわかると思う👀💦

実際、下の実行コードを追加して実行してみても、、、

let m_KakuStruct12_1 = M_KakuSampleStruct12_1(name: "M_Kaku堂")
print(m_KakuStruct12_1.name)
let m_KakuClass12_2 = M_KakuSampleClass12_2(name: "M_Kaku堂")
print(m_KakuClass12_2.name)
てな感じで、実行結果も全く一緒〜〜〜

と、ここまでだったら、もちろん

違いはない
(ようなサンプルを敢えて出してるから当たり前なんだけど、)

ここがどのプログラミング言語にも共通する視座なんだが、

似通っている部分が多いからこそ、違いをしっかり理解することが大事

につながってくる。じゃ、実際、

クラスと構造体の違いっちゃあ何なん🧐?

違い① 取り扱う値がValue TypeかReference Typeか ?

これだけだとちんぷんかんぷんだろうからここは、冒頭に出したリンクの解説をしっかり引用して、読みやすくまとめるね〜〜〜

構造体とクラスのインスタンスがコピーされるか、引数としてメソッドや関数に渡されるときに、動作に大きな違いが生じる。

・構造体のインスタンス=値型
・クラスのインスタンス=参照型

◾️構造体:

インスタンスがコピーされるかメソッドに渡される
👉インスタンス内に含まれるデータとともに、インスタンスの実際のコピーを作成。
👉コピーには、元の構造インスタンスと接続されていない独自のバージョンのデータ。
=実行中のアプリ内に構造体インスタンスの複数のコピーが存在。
👉それぞれが関連データの独自のローカル コピーを持つ。

⭐️構造体だと、1 つのインスタンスを変更しても、他のインスタンスには影響しない。

◾️クラス:

インスタンスがコピーされるか引数として渡される
👉やるのは、複製または渡されるのは、そのクラス インスタンスが存在するメモリ内の場所への参照だけ。
👉これらの参照を使用してインスタンスに加えられた変更をすべて、同じインスタンス上で実行。
=言い換えれば、クラス インスタンスは 1 つだけですが、それを指す参照が複数存在。

⭐️クラスだと、インスタンス データを変更すると、他のすべての参照のデータも変更される。

上の解説を見るだけでもわかると思うけど、

構造体で使うべきところで、クラスを使ってしまうと、大元のインスタンス自体の値を参照してるから、全て変更してしまう
👉処理結果が全然違うものになる危険性がある

ってことがわかると思う〜〜〜🕺

じゃ、実際にさっきのサンプルを使って、インスタンスで遊ぶと、、、

var m_KakuStruct12_3 = m_KakuStruct12_1
//構造体のインスタンスに代入
m_KakuStruct12_3.name = "D_Kaku堂"
print(m_KakuStruct12_1.name)
print(m_KakuStruct12_3.name)
var m_KakuClass12_3 = m_KakuClass12_2
//クラスのインスタンスに代入
m_KakuClass12_3.name = "D_Kaku堂"
print(m_KakuClass12_2.name)
print(m_KakuClass12_3.name)

を追加して、実行してみると〜〜〜〜

てな感じで
  • 構造体:大元のインスタンスは変わっていない

  • クラス:大元のインスタンスも変わってる

てのがわかると思う〜〜〜!

他にも、構造体は

  1. 継承とサブクラス化をサポートしない。👉ある構造が別の構造を継承できない。

  2. クラスと異なり、構造体には初期化解除 (deinit) メソッドを含めることもできない。

  3. 最後に、実行時にクラス インスタンスの型を識別できるが、構造体には当てはまらない。

って違いがあるらしい。

じゃ、クラスと構造体を使う勘どころはどこなん?

読み進めると〜〜〜

◇構造体:可能な限り構造体の使用をお勧め。

クラスよりも効率的であり、マルチスレッド コードでの使用がより安全であるため。

◇クラス:以下のいずれかの場合に使う。

  • 「継承が必要」

  • 「カプセル化されたデータのインスタンスが 1 つだけ必要」

  • 「インスタンスの初期化が解除されたときにリソースを解放するために追加の手順を実行する必要」

要は、

  • 基本的には、構造体を使う。

  • 構造体で賄えない場合だけ、クラスを使う。

なので、実は、少し前の記事

で、オイラは

クラスを実務であまり使うこと自体があまりない

ってすでに書いてるんだよね〜〜〜。だって、構造体の方が

  • インスタンスが書き換わらずに

  • 独自に安全に動かしてくれて、

  • クラスとほぼ、同じことをしてくれる

から。

よくネットの記事なんかで調べ物してる時に、

構造体で出来ることを、わざわざクラスでやってるって人
👉おそらくJavaみたいな他言語の感覚のまま、Swiftとの違いを理解せずにやってるか、構造体を知らないのかなみたいな記事を見たときには、

うーむ🧐

となってしまうし、しかも、前回の記事でも書いたとおり、

サンプルコードをこちらで実際に動かしてみると、インスタンスが書き換わるみたいなことをその記事で触れずに断言してる

なんて記事もザラにあるからね。

結構、クラスって使うことが少ないからね〜〜〜!

ま、クラスを使いこなせて、継承でやれれば、非常に強力なのは間違い無いんだけど、

インスタンスが書き換わる

ってゆー諸刃の剣だから。

別により安全に使えるモノが他にあるのに、敢えてここでクラスを使っているのは何故?

って感覚が、他人のコードを参照するときも、自分でコードを組む時も非常に大事。

これが今までの記事で滔々と書いてきてる、

運用や保守、メンテナンスや安全性の観点から、
経験者が敢えてやっていないことを、誰もやっていない=自分はより高度なことができるって、クラスを覚え始めた駆け出しさんは勘違いしてやってしまう。
しかも、コードを頭で書いてすぐに実行して検証しない(=インスペクションの基本を習慣付けてない)から、大量のゴミコードを書いて、本番検証直前とか本番リリース後に初めて実行して発覚し、涙目
😭

なんてことになるんだよね💦

っと、ちょっと犬の散歩の時間になったので、

書きかけだけど、一旦ここまでで話の区切りがついたから、

列挙型

については、犬の散歩から帰ってきて続きは書く〜〜〜

一旦ここまで読んでてくだされ〜〜〜
続きはまた後で🙇‍♂️

ただいま!

さてと、後半の列挙型に〜〜〜〜!

列挙型

ま、要は

構造体の中で扱う値をまとめて(列挙して)管理する

ってイメージかな🧐

サンプル(列挙型)

enum M_KakuBody12_5{
    case slim
    case medium
    case fat
}

てな感じで値を用意〜〜〜

func msgM_KakuBody12_5(body:M_KakuBody12_5){
    switch body {
    case.slim:
        print("痩せてる")
    case.medium:
        print("中肉中背")
    case.fat(dangerWeight):
        print("肥満")
    }
}

関数〜〜〜〜

msgM_KakuBody12_5(body: M_KakuBody12_5.medium)

で関数を実行すると、

てな感じで使うのが一番オーソドックスな感じ👀💦

こんな使い方もできるよって感じで〜〜〜

enum M_KakuBody12_6{
    case slim
    case medium
    case fat(dangerWeight:Double)
}
func msgM_KakuBody12_6(body:M_KakuBody12_6){
    switch body {
    case.slim:
        print("痩せてる")
    case.medium:
        print("中肉中背")
    case.fat:
        print("肥満")
    case.fat(let dangerWeight) where dangerWeight >= 72.2:
        print("危険な体重\(dangerWeight)を超えた肥満です。ダイエットしましょう!")
    }
}
msgM_KakuBody12_6(body: M_KakuBody12_6.fat(dangerWeight: 73.1))

てなコードを実行すると〜〜〜

てな感じで

先に条件を満たすものがSwiftは実行されちゃうのがSwitch文だったから、、、

後続の処理は実行されないので、

enum M_KakuBody12_6{
    case slim
    case medium
    case fat(dangerWeight:Double)
}
func msgM_KakuBody12_6(body:M_KakuBody12_6){
    switch body {
    case.slim:
        print("痩せてる")
    case.medium:
        print("中肉中背")
    case.fat(let dangerWeight) where dangerWeight >= 72.2:
        print("危険な体重\(dangerWeight)を超えた肥満です。ダイエットしましょう!")
    case.fat:
        print("肥満")
    }
}
msgM_KakuBody12_6(body: M_KakuBody12_6.fat(dangerWeight: 73.1))

でやってあげると〜〜〜

てな感じで実行できなくは無いんだけど〜〜〜

重複する値を処理するなんて、

可読性が落ちる上に、混乱とバグの種にしかならない

ので〜〜〜

enum M_KakuBody12_6{
    case slim
    case medium
    case fat(dangerWeight:Double)
}
func msgM_KakuBody12_6(body:M_KakuBody12_6){
    switch body {
    case.slim:
        print("痩せてる")
    case.medium:
        print("中肉中背")
    case.fat(let dangerWeight) where dangerWeight >= 72.2:
        print("危険な体重\(dangerWeight)を超えた肥満です。ダイエットしましょう!")
    default:
        break
    }
}
msgM_KakuBody12_6(body: M_KakuBody12_6.fat(dangerWeight: 73.1))

てな感じで、

しっかりdefaultまで書いてあげて

てな感じ

にするか

enum M_KakuBody12_6{
    case slim
    case medium
    case fat
    case danger(dangerWeight:Double)
}
func msgM_KakuBody12_6(body:M_KakuBody12_6){
    switch body {
    case.slim:
        print("痩せてる")
    case.medium:
        print("中肉中背")
    case.fat:
        print("肥満")
    case.danger(let dangerWeight) where dangerWeight >= 72.2:
        print("危険な体重\(dangerWeight)を超えた肥満です。ダイエットしましょう!")
    default:
        break
    }
}
msgM_KakuBody12_6(body: M_KakuBody12_6.danger(dangerWeight: 73.1))

てな感じで、

dangerって値をひとつ増やして

って感じにしてあげるのが安全だとは思うけどね〜〜〜

今回のコード(まとめ)

今回も長々と書いたようで、今回のコードをひとつにまとめると

/**:-------------------
 Essentials 第12章 構造体と列挙型
 ---------------------*/
struct M_KakuSampleStruct12_1{
    var name: String
    init(name: String) {
        self.name = name
    }
    func msgM_KakuName(){
        "オイラは、" + name + "だぞ!"
    }
}
class M_KakuSampleClass12_2{
    var name: String
    init(name: String) {
        self.name = name
    }
    func msgM_KakuName(){
        "オイラは、" + name + "だぞ!"
    }
}
let m_KakuStruct12_1 = M_KakuSampleStruct12_1(name: "M_Kaku堂")
print(m_KakuStruct12_1.name)
let m_KakuClass12_2 = M_KakuSampleClass12_2(name: "M_Kaku堂")
print(m_KakuClass12_2.name)

var m_KakuStruct12_3 = m_KakuStruct12_1
//構造体のインスタンスに代入
m_KakuStruct12_3.name = "D_Kaku堂"
print(m_KakuStruct12_1.name)
print(m_KakuStruct12_3.name)
var m_KakuClass12_3 = m_KakuClass12_2
//クラスのインスタンスに代入
m_KakuClass12_3.name = "D_Kaku堂"
print(m_KakuClass12_2.name)
print(m_KakuClass12_3.name)

enum M_KakuBody12_5{
    case slim
    case medium
    case fat
}
func msgM_KakuBody12_5(body:M_KakuBody12_5){
    switch body {
    case.slim:
        print("痩せてる")
    case.medium:
        print("中肉中背")
    case.fat:
        print("肥満")
    }
}
msgM_KakuBody12_5(body: M_KakuBody12_5.medium)

enum M_KakuBody12_6{
    case slim
    case medium
    case fat
    case danger(dangerWeight:Double)
}
func msgM_KakuBody12_6(body:M_KakuBody12_6){
    switch body {
    case.slim:
        print("痩せてる")
    case.medium:
        print("中肉中背")
    case.fat:
        print("肥満")
    case.danger(let dangerWeight) where dangerWeight >= 72.2:
        print("危険な体重\(dangerWeight)を超えた肥満です。ダイエットしましょう!")
    default:
        break
    }
}
msgM_KakuBody12_6(body: M_KakuBody12_6.danger(dangerWeight: 73.1))

実はこんなもん藁🤓

まとめ

タイトルにも書いたけど、今回は

💃構造体(struct)とクラス(class)の根本的な違い
を分かってもらえれば十分
🕺

なんか列挙型(enum)については、

駆け足で解説も薄い
structを繋がったenumの実践的な使い方も見たい

って感じたかもしれないけど、それは何故かと言うと、実は

までの導入編の記事で示した、いつも記事公開後にやってる

コードテンプレートのサンプル

で実践例

iOSApp17DevelopmentEssentials.swift

import SwiftUI

//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentials: Identifiable {
    var id: Int
    var title: String
    var view: ViewEnumiOSApp17DevelopmentEssentials
}

//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentials {
    case Ch1
    //じっくり13で追加
    case Ch2
    //じっくり13で追加
    case Ch3
    //じっくり15で追加
    case Ch4
    //じっくり16で追加
    case Ch5
    //じっくり17で追加
    case Ch6
    //じっくり18で追加
    case Ch7
    //じっくり19で追加
    case Ch8
    //じっくり20、21で追加
    case Ch9
    //じっくり22、23で追加
    case Ch10
}

//各項目に表示する文字列
let dataiOSApp17DevelopmentEssentials: [ListiOSApp17DevelopmentEssentials] = [
    ListiOSApp17DevelopmentEssentials(id: 1, title: "第1章", view: .Ch1),
    //じっくり13で追加
    ListiOSApp17DevelopmentEssentials(id: 2, title: "第2章", view: .Ch2),
    //じっくり13で追加
    ListiOSApp17DevelopmentEssentials(id: 3, title: "第3章", view: .Ch3),
    //じっくり15で追加
    ListiOSApp17DevelopmentEssentials(id: 4, title: "第4章", view: .Ch4),
    //じっくり16で追加
    ListiOSApp17DevelopmentEssentials(id: 5, title: "第5章", view: .Ch5),
    //じっくり17で追加
    ListiOSApp17DevelopmentEssentials(id: 6, title: "第6章", view: .Ch6),
    //じっくり18で追加
    ListiOSApp17DevelopmentEssentials(id: 7, title: "第7章", view: .Ch7),
    //じっくり19で追加
    ListiOSApp17DevelopmentEssentials(id: 8, title: "第8章", view: .Ch8),
    //じっくり20、21で追加
    ListiOSApp17DevelopmentEssentials(id: 9, title: "第9章", view: .Ch9),
    //じっくり22、23で追加
    ListiOSApp17DevelopmentEssentials(id: 10, title: "第10章", view: .Ch10)
]

struct iOSApp17DevelopmentEssentials: View {
    var body: some View {
        VStack {
            Divider()
            List (dataiOSApp17DevelopmentEssentials) { data in
                self.containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: data)
            }
            .edgesIgnoringSafeArea([.bottom])
        }
        .navigationTitle("iOS開発の章目次")
        .navigationBarTitleDisplayMode(.inline)
    }
    //タップ後に遷移先へ遷移させる関数
    func containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: ListiOSApp17DevelopmentEssentials) -> AnyView {
        switch dataiOSApp17DevelopmentEssentials.view {
        case .Ch1:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh1()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり13で追加
        case .Ch2:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh2()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり13で追加
        case .Ch3:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh3()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり15で追加
        case .Ch4:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh4()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり16で追加
        case .Ch5:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh5()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり17で追加
        case .Ch6:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh6()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり18で追加
        case .Ch7:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh7()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり19で追加
        case .Ch8:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh8()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり20、21で追加
        case .Ch9:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh9()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり22、23で追加
        case .Ch10:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh10()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
        }
    }
}
 #Preview  {
    iOSApp17DevelopmentEssentials()
}

てな感じで、functionでの使い方まで含めてサンプルを既に示していたからなんだよね〜〜〜

今回で実は、導入編から示していた、

コードの基本的な考え方は全て繋がった

って感じは理解してもらえたと思うし、これをしたかったから、

このコードコンプレートを導入編から作っていた

に他ならないんだけどね👀💦ま、

即断即決で、教科書とか教室事例だけ見て理解した気になってたような人は、

これまでのコードでオイラがなぜ、クラスを使っていなかったのか
👉ほとんど使う必要がねーからだよ

を悟ってもらえればそれで十分🕺

意識的に使わないのと、使いたくても使えない
のは意味合いが全然違う

からね。

さてと次回は、

SwiftUI実践編で、画面間の値の受け渡しなんかで必要になる

プロパティラッパー

をやってく〜〜〜〜🕺

記事公開後、

いつもやってる

に今回の記事もはめ込んでいこう

そろそろ、章の数も増えてきたし、どの章に何が書いてるかも目次で把握しておきたいので〜〜〜

TitleManageFile

を新規で追加〜〜〜〜

◾️TitleManageFile.swift

import Foundation
let essentialsChapter1NavigationTitle = "第1章"
let essentialsChapter1Title = "第1章 iOS 17 アプリ開発の基礎の概要"
let essentialsChapter2NavigationTitle = "第2章"
let essentialsChapter2Title = "第2章 Apple Developer Programに参加する方法"
let essentialsChapter3NavigationTitle = "第3章"
let essentialsChapter3Title = "第3章 Xcode 15 と iOS 17 SDK のインストール"
let essentialsChapter4NavigationTitle = "第4章"
let essentialsChapter4Title = "第4章 Xcode 15 プレイグラウンドの概要"
let essentialsChapter5NavigationTitle = "第5章"
let essentialsChapter5Title = "第5章 Swift データ型、定数、変数"
let essentialsChapter6NavigationTitle = "第6章"
let essentialsChapter6Title = "第6章 Swift 演算子と式"
let essentialsChapter7NavigationTitle = "第7章"
let essentialsChapter7Title = "第7章 迅速な制御フロー"
let essentialsChapter8NavigationTitle = "第8章"
let essentialsChapter8Title = "第8章 Swift Switch ステートメント"
let essentialsChapter9NavigationTitle = "第9章"
let essentialsChapter9Title = "第9章 Swift の関数、メソッド、クロージャ"
let essentialsChapter10NavigationTitle = "第10章"
let essentialsChapter10Title = "第10章 Swift オブジェクト指向プログラミングの基礎"
let essentialsChapter11NavigationTitle = "第11章"
let essentialsChapter11Title = "第11章 Swift のサブクラス化と拡張機能の概要"
let essentialsChapter12NavigationTitle = "第12章"
let essentialsChapter12Title = "第12章 Swift 構造と列挙の概要"

◾️CodeManageFile .swift(追加分)

let codeEssentials12 = """
/**:-------------------
 Essentials 第12章 構造体と列挙型
 ---------------------*/
struct M_KakuSampleStruct12_1{
    var name: String
    init(name: String) {
        self.name = name
    }
    func msgM_KakuName(){
        "オイラは、" + name + "だぞ!"
    }
}
class M_KakuSampleClass12_2{
    var name: String
    init(name: String) {
        self.name = name
    }
    func msgM_KakuName(){
        "オイラは、" + name + "だぞ!"
    }
}
let m_KakuStruct12_1 = M_KakuSampleStruct12_1(name: "M_Kaku堂")
print(m_KakuStruct12_1.name)
let m_KakuClass12_2 = M_KakuSampleClass12_2(name: "M_Kaku堂")
print(m_KakuClass12_2.name)

var m_KakuStruct12_3 = m_KakuStruct12_1
//構造体のインスタンスに代入
m_KakuStruct12_3.name = "D_Kaku堂"
print(m_KakuStruct12_1.name)
print(m_KakuStruct12_3.name)
var m_KakuClass12_3 = m_KakuClass12_2
//クラスのインスタンスに代入
m_KakuClass12_3.name = "D_Kaku堂"
print(m_KakuClass12_2.name)
print(m_KakuClass12_3.name)

enum M_KakuBody12_5{
    case slim
    case medium
    case fat
}
func msgM_KakuBody12_5(body:M_KakuBody12_5){
    switch body {
    case.slim:
        print("痩せてる")
    case.medium:
        print("中肉中背")
    case.fat:
        print("肥満")
    }
}
msgM_KakuBody12_5(body: M_KakuBody12_5.medium)

enum M_KakuBody12_6{
    case slim
    case medium
    case fat
    case danger(dangerWeight:Double)
}
func msgM_KakuBody12_6(body:M_KakuBody12_6){
    switch body {
    case.slim:
        print("痩せてる")
    case.medium:
        print("中肉中背")
    case.fat:
        print("肥満")
    case.danger(let dangerWeight) where dangerWeight >= 72.2:
        print("危険な体重\\(dangerWeight)を超えた肥満です。ダイエットしましょう!")
    default:
        break
    }
}
msgM_KakuBody12_6(body: M_KakuBody12_6.danger(dangerWeight: 73.1))
"""

◾️PointManageFile.swift(追加分)

let pointEssentials12 = """
クラスと構造体の違いっちゃあ何なん🧐?
違い① 取り扱う値がValue TypeかReference Typeか ?
構造体とクラスのインスタンスがコピーされるか、引数としてメソッドや関数に渡されるときに、動作に大きな違いが生じる。
・構造体のインスタンス=値型
・クラスのインスタンス=参照型
◾️構造体:
インスタンスがコピーされるかメソッドに渡される
👉インスタンス内に含まれるデータとともに、インスタンスの実際のコピーを作成。
👉コピーには、元の構造インスタンスと接続されていない独自のバージョンのデータ。
=実行中のアプリ内に構造体インスタンスの複数のコピーが存在。
👉それぞれが関連データの独自のローカル コピーを持つ。

⭐️構造体だと、1 つのインスタンスを変更しても、他のインスタンスには影響しない。

◾️クラス:
インスタンスがコピーされるか引数として渡される
👉やるのは、複製または渡されるのは、そのクラス インスタンスが存在するメモリ内の場所への参照だけ。
👉これらの参照を使用してインスタンスに加えられた変更をすべて、同じインスタンス上で実行。
=言い換えれば、クラス インスタンスは 1 つだけですが、それを指す参照が複数存在。

⭐️クラスだと、インスタンス データを変更すると、他のすべての参照のデータも変更される。

上の解説を見るだけでもわかると思うけど、
構造体で使うべきところで、クラスを使ってしまうと、大元のインスタンス自体の値を参照してるから、全て変更してしまう
👉処理結果が全然違うものになる危険性がある
構造体:大元のインスタンスは変わっていない
クラス:大元のインスタンスも変わってる

◇構造体:可能な限り構造体の使用をお勧め。
クラスよりも効率的であり、マルチスレッド コードでの使用がより安全であるため。
◇クラス:以下のいずれかの場合に使う。
「継承が必要」
「カプセル化されたデータのインスタンスが 1 つだけ必要」
「インスタンスの初期化が解除されたときにリソースを解放するために追加の手順を実行する必要」

要は、
  基本的には、構造体を使う。構造体で賄えない場合だけ、クラスを使う。

運用や保守、メンテナンスや安全性の観点から、
経験者が敢えてやっていないことを、誰もやっていない=自分はより高度なことができるって、クラスを覚え始めた駆け出しさんは勘違いしてやってしまう。
しかも、コードを頭で書いてすぐに実行して検証しない(=インスペクションの基本を習慣付けてない)から、大量のゴミコードを書いて、本番検証直前とか本番リリース後に初めて実行して発覚し、涙目😭
なんてことになるんだよね💦
💃構造体(struct)とクラス(class)の根本的な違いを分かってもらえれば十分🕺
"""

◾️URLManageFile.swift(追加分)

let urlEssentials12 = "https://note.com/m_kakudo/n/n351fa9ffa7b2"

◾️iOSApp17DevelopmentEssentials.swift

import SwiftUI

//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentials: Identifiable {
    var id: Int
    var title: String
    var view: ViewEnumiOSApp17DevelopmentEssentials
}

//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentials {
    case Ch1
    //じっくり13で追加
    case Ch2
    //じっくり13で追加
    case Ch3
    //じっくり15で追加
    case Ch4
    //じっくり16で追加
    case Ch5
    //じっくり17で追加
    case Ch6
    //じっくり18で追加
    case Ch7
    //じっくり19で追加
    case Ch8
    //じっくり20、21で追加
    case Ch9
    //じっくり22、23で追加
    case Ch10
    //じっくり24で追加
    case Ch11
    //じっくり25で追加
    case Ch12
}

//各項目に表示する文字列
let dataiOSApp17DevelopmentEssentials: [ListiOSApp17DevelopmentEssentials] = [
    ListiOSApp17DevelopmentEssentials(id: 1, title: essentialsChapter1Title, view: .Ch1),
    //じっくり13で追加
    ListiOSApp17DevelopmentEssentials(id: 2, title: essentialsChapter2Title, view: .Ch2),
    //じっくり13で追加
    ListiOSApp17DevelopmentEssentials(id: 3, title: essentialsChapter3Title, view: .Ch3),
    //じっくり15で追加
    ListiOSApp17DevelopmentEssentials(id: 4, title: essentialsChapter4Title, view: .Ch4),
    //じっくり16で追加
    ListiOSApp17DevelopmentEssentials(id: 5, title: essentialsChapter5Title, view: .Ch5),
    //じっくり17で追加
    ListiOSApp17DevelopmentEssentials(id: 6, title: essentialsChapter6Title, view: .Ch6),
    //じっくり18で追加
    ListiOSApp17DevelopmentEssentials(id: 7, title: essentialsChapter7Title, view: .Ch7),
    //じっくり19で追加
    ListiOSApp17DevelopmentEssentials(id: 8, title: essentialsChapter8Title, view: .Ch8),
    //じっくり20、21で追加
    ListiOSApp17DevelopmentEssentials(id: 9, title: essentialsChapter9Title, view: .Ch9),
    //じっくり22、23で追加
    ListiOSApp17DevelopmentEssentials(id: 10, title: essentialsChapter10Title, view: .Ch10),
    //じっくり24で追加
    ListiOSApp17DevelopmentEssentials(id: 11, title: essentialsChapter11Title, view: .Ch11),
    //じっくり25で追加
    ListiOSApp17DevelopmentEssentials(id: 12, title: essentialsChapter12Title, view: .Ch12)
]

struct iOSApp17DevelopmentEssentials: View {
    var body: some View {
        VStack {
            Divider()
            List (dataiOSApp17DevelopmentEssentials) { data in
                self.containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: data)
            }
            .edgesIgnoringSafeArea([.bottom])
        }
        .navigationTitle("iOS開発の章目次")
        .navigationBarTitleDisplayMode(.inline)
    }
    //タップ後に遷移先へ遷移させる関数
    func containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: ListiOSApp17DevelopmentEssentials) -> AnyView {
        switch dataiOSApp17DevelopmentEssentials.view {
        case .Ch1:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh1()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり13で追加
        case .Ch2:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh2()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり13で追加
        case .Ch3:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh3()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり15で追加
        case .Ch4:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh4()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり16で追加
        case .Ch5:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh5()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり17で追加
        case .Ch6:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh6()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり18で追加
        case .Ch7:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh7()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり19で追加
        case .Ch8:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh8()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり20、21で追加
        case .Ch9:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh9()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり22、23で追加
        case .Ch10:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh10()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり24で追加
        case .Ch11:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh11()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり25で追加
        case .Ch12:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh12()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
        }
    }
}

#Preview {
    iOSApp17DevelopmentEssentials()
}

◾️iOSApp17DevelopmentEssentialsCh12.swift

import SwiftUI

//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentialsCh12: Identifiable {
    var id: Int
    var title: String
    var view: ViewEnumiOSApp17DevelopmentEssentialsCh12
}

//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentialsCh12 {
    case Sec1
}

//各項目に表示するリスト項目
let dataiOSApp17DevelopmentEssentialsCh12: [ListiOSApp17DevelopmentEssentialsCh12] = [
    ListiOSApp17DevelopmentEssentialsCh12(id: 1, title: "第1節", view: .Sec1)
]

struct iOSApp17DevelopmentEssentialsCh12: View {
    var body: some View {
        VStack {
            Divider()
            List (dataiOSApp17DevelopmentEssentialsCh12) { data in
                self.containedViewiOSApp17DevelopmentEssentialsCh12(dataiOSApp17DevelopmentEssentialsCh12: data)
            }
            .edgesIgnoringSafeArea([.bottom])
        }
        .navigationTitle(essentialsChapter12NavigationTitle)
        .navigationBarTitleDisplayMode(.inline)
    }
    //タップ後に遷移先へ遷移させる関数
    func containedViewiOSApp17DevelopmentEssentialsCh12(dataiOSApp17DevelopmentEssentialsCh12: ListiOSApp17DevelopmentEssentialsCh12) -> AnyView {
        switch dataiOSApp17DevelopmentEssentialsCh12.view {
        case .Sec1:
            return AnyView(NavigationLink (destination: Essentials12()) {
                Text(dataiOSApp17DevelopmentEssentialsCh12.title)
            })
        }
    }
}

#Preview {
    iOSApp17DevelopmentEssentialsCh12()
}

◾️Essentials12.swift

import SwiftUI
import WebKit

struct Essentials12: View {
    var body: some View {
        VStack{
            TabView {
                Essentials12Code()
                    .tabItem {
                        Image(systemName: codeImageTab)
                        Text(codeTextTab)
                    }
                Essentials12Points()
                    .tabItem {
                        Image(systemName: pointImageTab)
                        Text(pointTextTab)
                    }
                Essentials12WEB()
                    .tabItem {
                        Image(systemName: webImageTab)
                        Text(webTextTab)
                    }
            }
        }
    }
}

#Preview {
    Essentials12()
}

struct Essentials12Code: View {
    var body: some View {
        ScrollView{
            Text(codeEssentials12)
        }
    }
}

#Preview {
    Essentials12Code()
}

struct Essentials12Points: View {
    var body: some View {
        ScrollView{
            Text(pointEssentials12)
        }
    }
}

#Preview {
    Essentials12Points()
}

struct Essentials12WebView: UIViewRepresentable {
    let searchURL: URL
    func makeUIView(context: Context) -> WKWebView {
        let view = WKWebView()
        let request = URLRequest(url: searchURL)
        view.load(request)
        return view
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        
    }
}

struct Essentials12WEB: View {
    private var url:URL = URL(string: urlEssentials12)!
    var body: some View {
        Essentials12WebView(searchURL: url)
    }
}

#Preview {
    Essentials12WEB()
}

実行後

てな感じに目次が変わって〜〜〜

いつもどおりに

コード〜〜〜
ポイント〜〜〜
リンク〜〜〜

以上。

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