ClassはSwift言語における参照型(reference type)であり、そのインスタンスは代入時にコピーされるのではなく、参照によって渡されます。構造体とは異なり、クラスは継承、デイニシャライゼーション、メモリ管理のための自動参照カウントARCをサポートしています。Swift Programming Language Guide, 2026によると、AppleのUIフレームワーク(UIKit、AppKit)で作業し、オブジェクトの共有アイデンティティを必要とするパターンを実装するにはclassが必要です。classとstructの選択は、Swiftにおける主要なアーキテクチャ上の決定の1つです。
重要なポイント
Class — 継承と動的ディスパッチをサポートし、プロパティとメソッドを1つのエンティティにグループ化するSwiftの複合参照型です。structとは異なり、クラスインスタンスはヒープに作成され、変数はデータそのものではなく、このインスタンスへの参照を格納します。
クラス変数を別の変数に代入すると、両方がメモリ内の同じオブジェクトを参照します。一方の参照を介した変更は、もう一方の参照を通じて表示されます — これは参照型の基本的なプロパティであり、デリゲーション、監視、共有状態のパターンで使用されます。
Apple Swiftドキュメントによると、すべてのUIコンポーネントがUIViewとUIViewControllerから継承されるUIKitおよびAppKitで作業する唯一の方法はクラスです。さらに、オブジェクトのアイデンティティ(1つのオブジェクトへの2つの参照)と制御されたライフタイムを必要とするパターンを実装するには、クラスが必要です。
参照型のセマンティクス — クラスの主要なプロパティです。クラスを新しい変数に代入するとき、Swiftはデータではなく参照をコピーします。同じインスタンスを参照するすべての変数は、その現在の状態を確認し、変更できます。この動作は、各変数が独立したコピーを取得する値型とは根本的に異なります。
// 参照型セマンティクスの例
class User {
var name: String
init(name: String) { self.name = name }
}
let user1 = User(name: "Alice")
let user2 = user1 // user2 — 同じ参照
user2.name = "Bob"
print(user1.name) // "Bob" — user2経由で変更!
// ===演算子によるアイデンティティチェック
print(user1 === user2) // true — 同じオブジェクト
===(アイデンティティ)および !== 演算子は、2つの変数が同じクラスインスタンスを参照しているかどうかを確認します。これは、プロパティ値を比較する ==(等価性)とは異なります。=== 演算子はstructでは使用できません — 値型にはアイデンティティがありません。
継承 — クラスが親クラスのプロパティとメソッドを引き継ぐことができるメカニズムです。Swiftでは、クラスは1つの親からのみ継承できます(単一継承)が、複数のプロトコルを実装できます。overrideキーワードを使用すると、継承されたメソッドまたはプロパティをオーバーライドできます。
別のクラスから継承しないクラスは、自動的に基底クラスになります(NSObjectと混同しないでください)。サブクラスは、名前の後にコロンを付けて親を指定します。サブクラスが親のメソッドをオーバーライドする場合、親の動作を維持するためにsuper.メソッド()を呼び出す必要があります — これはコンパイラの要件です。
classの前のキーワードfinalは継承を防ぎます。コンパイラは静的ディスパッチを通じてfinalクラスのメソッド呼び出しを最適化し、パフォーマンスを向上させます。拡張を意図していないクラスにはfinalを使用してください — これにより意図が文書化され、コードが高速化されます。
// Swiftにおけるクラス継承の例
class Vehicle {
var speed: Double = 0
func description() -> String {
"Speed: \(speed) km/h"
}
}
// CarはVehicleを継承
class Car: Vehicle {
var brand: String = "Unknown"
override func description() -> String {
"\(brand) - \(speed) km/h"
}
}
// Finalクラス — 継承を防ぐ
final class ElectricCar: Car {
var batteryLevel: Double = 100
}
let tesla = ElectricCar()
tesla.brand = "Tesla"
tesla.speed = 120
print(tesla.description()) // "Tesla - 120.0 km/h"
継承は強力ですが責任を伴うメカニズムです。深い階層(5レベル以上)はメンテナンスとテストを複雑にします。継承なしで機能を再利用するには、拡張とプロトコル指向プログラミングを備えたプロトコルを使用してください — Appleが深いクラス階層の代替として推奨するアプローチです。
Swiftはクラスのメモリ管理にARC(自動参照カウント)を使用します。各クラスインスタンスには強参照カウンターがあります。新しい強参照が作成されるとカウンターが増加し、破棄されると減少します。カウンターがゼロになると、メモリが解放されます。
Deinit — クラスインスタンスが解放される前に自動的に呼び出されるメソッドです。リソースを解放します:ファイルを閉じ、通知の購読を解除し、タイマーを停止します。Deinitはクラスでのみ使用可能です — 構造体と列挙型にはありません。
強参照サイクル(retain cycles)を防ぐために、Swiftはweak参照とunowned参照を提供します。Weakはオプショナル参照で、オブジェクトが解放されると自動的にnilになります。Unownedは非オプショナルですが、現在のコンテキストよりも長く生存することが保証されているオブジェクトを参照します。典型的なretain cycleは親子関係で発生します:子が親への強参照を保持します。
// ARCと弱参照の例
class Parent {
var name: String
var child: Child?
init(name: String) { self.name = name }
deinit { print("\(name) が解放されました") }
}
class Child {
var name: String
weak var parent: Parent? // weakはretain cycleを防ぐ
init(name: String) { self.name = name }
deinit { print("\(name) が解放されました") }
}
var parent: Parent? = Parent(name: "Anna")
parent?.child = Child(name: "Mia")
parent?.child?.parent = parent
parent = nil // 両方のオブジェクトが解放されました!
// weakがないとretain cycleが発生する
子オブジェクトから親への参照およびクロージャのキャプチャリストには常にweakを使用してください。Unownedは、オブジェクトが現在のコンテキストよりも長く生存することが確実な場合にのみ使用してください — unownedの誤用は、解放されたメモリにアクセスするとクラッシュを引き起こす可能性があります。
classとstructの選択は、パフォーマンス、スレッドセーフティ、API設計に影響を与えるアーキテクチャ上の決定です。各ケースで正しい決定を下すために、主要な違いの表を検討してください。
| 特性 | class | struct |
|---|---|---|
| 型 | 参照型 | 値型(コピー) |
| メモリ | ヒープ + ARC | スタック / インライン |
| 継承 | サポート(単一親) | サポートしない |
| Deinit | あり | なし |
| アイデンティティ (===) | サポート | サポートしない |
| Mutating | 不要(常にmutating) | mutatingでのみ |
| Memberwise init | 生成されない | 自動生成される |
| スレッドセーフティ | 保証されない(共有状態) | 保証される(コピー) |
共有アイデンティティ(コードの複数の部分が1つのオブジェクトで動作する)、継承、またはObjective-Cランタイムとの連携が必要な場合はクラスを使用してください。それ以外の場合は、構造体が推奨されます — より高速で、マルチスレッドコードで安全であり、メモリ管理が不要です。
Appleがデフォルトでstructを使用することを推奨しているにもかかわらず、いくつかの特定のシナリオではクラスが必要です。実践的な例を挙げてそれぞれを検討しましょう。
iOSおよびmacOSのすべてのUIコンポーネントは、UIView(iOS)またはNSView(macOS)から継承するクラスです。UIViewControllerを構造体に置き換えることはできません — リソースを解放するために継承とdeinitが必要です。UIKitで作業する場合は、コントローラー、ビュー、およびそのデリゲートにクラスを使用してください。
シングルトンパターン(アプリケーション全体で1つのクラスインスタンス)は参照セマンティクスを必要とします。マネージャー:NetworkManager、SettingsManager、AnalyticsService — 通常は共有のstaticプロパティを持つクラスとして実装されます。構造体は各コピーが独立したインスタンスになるため適していません。
オブジェクトが単一の真実の源であり、モジュール間で参照によって渡される必要がある場合は、classを使用してください。これは、ある場所でオブジェクトを変更したことがすべての依存コンポーネントに表示される必要がある状態管理に適用されます。SwiftUIのObservableObjectの場合、クラスが必須です。
// ObservableObject — SwiftUIにclassが必要
import SwiftUI
class AppViewModel: ObservableObject {
@Published var isLoggedIn: Bool = false
@Published var username: String = ""
func login(user: String) {
isLoggedIn = true
username = user
}
}
// SwiftUI Viewでの使用
struct ContentView: View {
@StateObject var viewModel = AppViewModel()
var body: some View {
Text(viewModel.isLoggedIn ? "ようこそ" : "ログイン")
}
}
ルール:オブジェクトにアイデンティティ(2つの参照 -> 1つのオブジェクト)が必要な場合、単一インスタンスである場合、またはUIKit/Objective-Cで動作する必要がある場合 — classを選択してください。オブジェクトが単にデータを含む場合 — structを選択してください。
よくある質問
Classは継承、デイニシャライゼーション、メモリ管理のためのARCをサポートするSwiftの参照型です。クラスインスタンスはヒープに格納され、変数はオブジェクトへの参照を含み、そのコピーではありません。
Class — 参照型(参照で渡される)、継承とdeinitをサポート。Struct — 値型(コピーされる)、継承はサポートしないが、プロトコルを実装し、memberwise initを自動的に取得。Swiftはデフォルトの型としてstructを推奨しています。
ARC(自動参照カウント) — Swiftクラスのメモリ管理メカニズム。各インスタンスには強参照カウンターがあります。カウンターがゼロになるとメモリが解放されます。weak参照とunowned参照は、オブジェクト間のretain cyclesを防ぎます。
Deinit — メモリが解放される前に自動的に呼び出されるクラスメソッド。ファイルを閉じたり、通知の購読を解除したり、その他のクリーンアップ操作に使用されます。Deinitはクラスでのみ使用可能で、構造体にはありません。
UIKit/AppKitのUIコンポーネント、シングルトン、SwiftUIのObservableObject、共有アイデンティティを持つオブジェクト(delegate、observer)、およびObjective-Cランタイムで作業する場合はclassを使用してください。データモデル、DTO、設定にはstructが推奨されます。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。