Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
84
Help us understand the problem. What is going on with this article?
@chanNaru

【swift】イラストで分かる!classとstructの違いについて【初心者向け】

More than 1 year has passed since last update.

概要

👨‍🏫「ここってstructで書いてるけど、意味とかあって書いた?」
🙍‍♀️「!」
🙍‍♀️「定数を並べただけのものだったので、そういうのはstructの方がいいかなって..」
👨‍🏫「ほーん」
🙍‍♀️「正直言うと意味なくstructにしました」
👨‍🏫「ほーん」

ということがあったので、
改めてそれぞれどういう違いがあるのかを改めて整理してみました٩( 'ω' )و

それぞれの特徴

先に、それぞれについての特徴をパッと簡単に確認。
イメージは@loveeさんの記事を参考にしています。

ポイントとなるのは参照型か値型か、ってところですね🤔
また、コードを書いてみると、

struct
struct test {
    let cat: String
    let dog: String
}
/// -> 怒られない
class
class test {
    let cat: String
    let dog: String
}
/// -> 💥怒られる: Class 'test' has no initializers

クラスの方は初期化して!と怒られます。

実装して違いを見てみよう

class Cat {
    var like: String = "魚"
}
struct Dog {
    var like: String = "肉"
}

クラスで作られた魚好きの🐱とストラクトで作られた肉好きの🐶。

let tama: Cat = Cat()
let pochi: Dog = Dog()

func initWithData(_ data: Cat) {
 var data = data
 data.like = "マタタビ"
}

func initWithData2(_ data: Dog) {
 var data = data
 data.like = "マタタビ"
}

initWithData(tama)
initWithData2(pochi)

それぞれを連れてきてマタタビをあげてみると、

print(tama.like) // →"マタタビ"
print(pochi.like) // →"肉"

という結果に。🐱は好物が魚からマタタビに書き換わりました。

この実行結果から見ても、
class🐱は参照渡しなので、
var data = dataで、cat()そのものが渡されており、
var tama: cat = cat()自体が書き換わっているのに対して、
struct🐶は値渡しなので、
var data = dataで、cat()のコピーが作られ、
var pochi: dog = dog()自体が書き換わることはありません٩( 'ω' )و

ご存知の通り?initで初期値を入れることもできます。

class Cat {
    var like: String
    init(like: String) {
        self.like = like
    }
}
struct Dog {
    var like: String
    init(like: String) {
        self.like = like
    }
}
let tama: cat = Cat(like: "魚")
let pochi: dog = Dog(like: "肉")

ただし、その場合は、

let tama: cat = Cat(like: "魚")
tama.like = "肉" // →"肉"
let pochi: dog = Dog(like: "肉")
pochi.like = "魚" // ERROR

となります。struct🐶は好き嫌いが激しいです。
これはstructは値渡しなので、例えて言うと、

実体(=参照型class🐱)に直接手を加えることはできるけど、
写真(=値型struct🐶)に直接手を加えることはできない(写真の加工とかは置いておいて..)
といったイメージで大丈夫だと思います٩( 'ω' )و

公式ドキュメント

Appleの公式ドキュメントにstructは以下の場面で使い、それ以外はclassを使用するのが良いという内容が書かれていました。(意訳)

  • structの目的は比較的単純なデータ値をカプセル化すること
  • インスタンスを代入または受け渡しするときは、カプセル化された値が参照ではなくコピーされる
  • structに格納されているプロパティはすべて値型であり、参照ではなくコピーされる
  • structは、他の既存の型からプロパティや動作を継承する必要はありません。

ついでに

ちなみにそもそも発端となったコードは以下のようなものでした。

/// カラーの拡張(各所で、'let xxx = UIColor.mainGreenColor()'などと呼ぶ)
extension UIColor {
    // UIColorへの変換
    private func hexColor(_ str: String) -> UIColor {
        // (略)ここでカラーコード→UIColorに変える
        return UIColor(red: x, green: x, blue: x, alpha: 1.0)
    }
    // メインカラー       
    @objc static func mainGreenColor() -> UIColor {
        return self.hexColor(ColorCode.mainGreenColor)
    }
    // バッチカラー
    @objc static func badgeColor() -> UIColor {         
        return self.hexColor(ColorCode.badgeColor)
    }               
    // カラーコード
    struct ColorCode { // ←⭐️ここ   
        static let mainGreenColor: String = "#219E62"
        static let badgeColor: String = "#FF3300"
    }
}

色々調べてみた結果としては、
「staticな変数だけをまとめたものだから、メモリを無駄に増やさないようにっていう観点でもclassを使うのがベターだな」
っていうのが理解できました😇

最後に

ちゃんと調べる前は、チームによってとか人によって結構好みの問題もあるんじゃないかとかって思っていたのですが、
ちゃんと意味を持った使い分けも出来るんだな、と改めて理解できました٩( 'ω' )و

最後の最後

delegate=selfとか諸々すごく端折っているのですが、
以下のコードのようなCatViewDataというデータを常に更新して保持するクラスを作り持ち回すという構成で書いたときに、
あまり考えずにstructを使った結果、データが全く更新(保持)されずで小一時間悩んで「んあぁぁああああ😭!!!!!」ってなったことがあったので、
結構動作に影響する場面があったりもするのでそいう意味でもちゃんと考えて使っていきたいところです。


/// データ保持用クラス
// ↓structにすると、データを書き換えても値型でコピーされるだけなので、呼び直すタイミングで消えてしまう
class CatViewData {
    var name: String
    init(name: String) {
        self. name = name
    }
}

/// VC&Table用クラス
class TableViewDataSource: UIViewController, UITableViewDelegate, UITableViewDataSource {
    var viewData: CatViewData

    // ここでテーブルを更新
    func reloadTable(viewData: CatViewData) {
        tableView.reloadData()
        self.initWithData(viewData: viewData)
    }

    // ここでデータを更新
    func initWithData(viewData: CatViewData) {
        self.viewData = viewData
    }

    // ~~略~~

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CatCell") as! CatCell
    cell.setSettingCell(viewData: viewData)
    return cell
    }

       func reloadTable(viewData: CatViewData) {

      }
}

/// 表示用のセルクラス
protocol delegate {
      func reloadTable(viewData: CatViewData)
}
class CatCell: UITableViewCell {
     var viewData: CatViewData
     func setSettingCell(viewData: viewData) {
          self.viewData = viewData
    }
    @IBAction func buttonTapped(sender:AnyObject) {
        self.viewData = "(textViewから入力したデータ)"
        delegate.reloadTable(viewData: self.viewData)
    }
}

簡単ですが以上です。
誤っている箇所等ありましたらコメントいただけると幸いです。

84
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
chanNaru
バイトル(dip)→ジモティー swiftでiOSアプリ開発してます、iOSDCに登壇したりしてますʕ•ᴥ• ʔ お絵描きと世界史とドライブが好きです (twitterは完全な車垢><)
jmty
日本最大のクラシファイドサイト「ジモティー」を開発・運営するスタートアップ

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
84
Help us understand the problem. What is going on with this article?