0
1

Swiftのprotocol,enum,struct,classをまとめてみた

Posted at

はじめに

なんとなく使ってるprotocol enum struct classについてガチガチに調べました

目次

  1. protocolに関して
  2. enumに関して
  3. structに関して
  4. classに関して
  5. 値型参照型の違い
  6. structとclassの使い分け
  7. 用語

protocolに関して

参考にしたサイトでは「Swiftのプロトコルは、型のインターフェースを定義するものです。」と書いてありました。

  • 型のインターフェイスとは?
    たとえば、ある型(クラスや構造体)がどんな機能や性質をもっているか説明するもの
    よくわからないので実装例を見ていきます。
protocol Playable {
  func play()
}

struct Toy: Playable {
  func play() {
    print("おもちゃで遊ぶよん")
  }
}

さっき書いた説明に当てはめると「構造体(Toy)がplay()という機能/性質を持ってるよ」ってことです。

  • でもこれPlayableつくんなくて良くない?
    おっしゃる通りだと思います。
    しかし、処理が増えてくるとprotocolを作らないとめんどくさくなります
    例えばprotocolなしだと
struct Toy {
    func play() {
        print("おもちゃで遊ぶよん")
    }
}

struct Game {
    func play() {
        print("ゲームで遊ぶよん")
    }
}

// `Toy`型専用の関数
func startPlayingToy(toy: Toy) {
    toy.play()
}

// `Game`型専用の関数
func startPlayingGame(game: Game) {
    game.play()
}

let toy = Toy()
let game = Game()

startPlayingToy(toy: toy)  // 出力: おもちゃで遊ぶよん
startPlayingGame(game: game) // 出力: ゲームで遊ぶよん

startPlayingToy()startPlayingGamestartPlayingGameの2つ関数を作らないといけないのですがprotocolを使うと

protocol Playable {
    func play()
}

struct Toy: Playable {
    func play() {
        print("おもちゃで遊ぶよん")
    }
}

struct Game: Playable {
    func play() {
        print("ゲームで遊ぶよん")
    }
}

func startPlaying(item: Playable) {
    item.play()
}

let toy = Toy()
let game = Game()

startPlaying(item: toy)  // 出力: おもちゃで遊ぶよん
startPlaying(item: game) // 出力: ゲームで遊ぶよん

1つの関数で済むようになります。

  • その他、ObservableObjectDecodableもprotocolの1つですね。

enumに関して

列挙型とは、値型の一種で、関連する値のグループに共通の型を定義してまとめたもの
これは例を見れば簡単です

enum Game {
  case monsterHunter
  case dragonQuest
  case smashBros
  case splatoon
}

こんな感じでGameの中にモンハン、ドラクエ、スマブラ、スプラトゥーンが入ってる感じですね。

func printGameMessage(game: Game) {
    switch game {
    case .monsterHunter:
        print("モンスターハンターで狩りをしよう!")
    case .dragonQuest:
        print("ドラゴンクエストのRTAが待っている!")
    case .smashBros:
        print("スマッシュブラザーズでバトルしていらつこう!")
    case .splatoon:
        print("スプラトゥーンで味方にイラつこう!")
    }
}

使う時はこんな感じでGame.(caseで決めた値)のように設定します。

structに関して

簡単にいうと継承できない値渡しの、変数や関数をまとめた固まりみたいなもの

classに関して

継承できる参照渡しの、変数や関数をまとめた固まりみたいなもの

値型参照型の違い

ここまで読んでくれてる人に「値渡しやら参照渡しやらうっとうしいわ」みたいなことを言われそうなのでまずこちらを解説していきます。
まずは例から
値型

struct Woman {
  var name: String
  var isCute: Bool
}

var woman1 = Woman(name: "長濱ねる", isCute: true)

var woman2 = woman1

woman2.name = "フワちゃん"
woman2.isCute = false

print("woman1")
print(woman1)
print("woman2")
print(woman2)

上の例で実行すると

woman1
Woman(name: "長濱ねる", isCute: true)
woman2
Woman(name: "フワちゃん", isCute: false)

と出力されます。
参照型
一方で

class Woman {
  var name: String
  var isCute: Bool
  
  init(name: String, isCute: Bool) {
    self.name = name
    self.isCute = isCute
  }
}

var woman1 = Woman(name: "長濱ねる", isCute: true)

var woman2 = woman1

woman2.name = "フワちゃん"
woman2.isCute = false

print("woman1: \(woman1.name), isCute: \(woman1.isCute)")
print("woman2: \(woman2.name), isCute: \(woman2.isCute)")

structで書いていたところをclassで書き直すと

woman1: フワちゃん, isCute: false
woman2: フワちゃん, isCute: false

とprintされます。

まとめ
違いが分かりましたでしょうか?

structはvar woman2 = woman1のタイミングでwoman1のコピーを渡してる感じです。
コピーされた紙に書いたものはコピーする前の紙には反映されないですよね?
それと同じことをしてます。

classはvar woman2 = woman1のタイミングでコピーではなくそのものを渡してます。
コピー取らずにそのままの紙に書き込んでるイメージです。
なので

woman2.name = "フワちゃん"
woman2.isCute = false

の時点でwoman1も書き換わり、お前は偉くないので⚪︎んでくださーいされます。

structclassの使い分け

classを使うとき

  • ViewModel・・・理由は、画面の状態やデータを複数のViewで共有したり、変更を他の場所に通知する必要があるため
  • Usecase・・・理由は、ビジネスロジックを持ち、状態の変更や複数箇所で共有が必要になるためclassが適している
  • Repository・・・理由は、APIやデータベースとのやり取りを行い、同じインスタンスでデータの管理が必要なことが多いためclassが適している
  • APIClient・・・理由は、APIとの通信を管理し、単一のインスタンスでネットワーク接続やリクエストの管理を行う必要があるためclassが適している

structを使うとき

  • Model・・・理由は、データを保持するだけの役割が多く、軽量なデータ構造を扱うため、コピーで扱えるstructが適している

あくまで一般的になので状況に応じて臨機応変に変えていくことが大事(丸投げ)

用語

参考文献

protocol
https://swift.codelly.dev/guide/%E3%83%97%E3%83%AD%E3%83%88%E3%82%B3%E3%83%AB/

enum
https://swift.codelly.dev/guide/%E5%88%97%E6%8C%99%E5%9E%8B/

struct
https://qiita.com/yuinchirn/items/98b568d595650eca3334
https://qiita.com/motoki418/items/156182ec8750e7b567b9
https://zenn.dev/egg_head/articles/fd48844e86ad7b

値型参照型の違い

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1