Swift 5.8 日本語化計画 : Swift 5.8
構造体とクラス
構造体とクラス は、汎用で、プログラムのコードをビルドするとブロックになる柔軟な構築物です。プロパティとメソッドを定義し、定数、変数、そして関数を定義するのと同じ構文を使って、構造体とクラスに、機能を追加します。
他のプログラミング言語とは異なり、Swift では、カスタム構造体やカスタムクラスのために別のインタフェースや実装ファイルを作成する必要はありません。Swift では、構造体やクラスを単一のファイル内で定義し、そのクラスまたは構造体への外部インタフェースは使用する他のコードに自動的で使用可能になります。
構造体とクラスを比較
Swift の構造体およびクラスは多くの共通点を持っています。両方とも以下のことができます。
- 値を格納するプロパティの定義
- 機能を提供するメソッドの定義
- サブスクリプト構文を使用する値へのアクセスを提供するためのサブスクリプトの定義
- 初期状態を設定するためのイニシャライザの定義
- デフォルトの実装を超えてそれらの機能を拡大できるように拡張
- ある種の標準機能を提供するためにプロトコルに準拠
詳細については、プロパティ、メソッド、サブスクリプト、初期化、拡張機能、および プロトコル を参照してください。
クラスには、構造体にはない追加の機能があります。
- 継承は、あるクラスが別のクラスの特性を継承する事を可能にします。
- 型キャストは、実行時にクラスインスタンスの型をチェックして解釈することを可能にします。
- Deinitializers は、それが割り当てたすべてのリソースを解放する事をクラスのインスタンスに可能にします。
- 参照カウントは、クラスインスタンスに対して複数の参照を可能にします。
詳細については、継承 、型キャスト、デイニシャライザ、および 自動参照カウント を参照してください。
クラスがサポートする追加の機能は複雑さが増すコストがあります。一般的なガイドラインとして、構造体を好む理由が簡単であるという理由であり、適切で必要なときにクラスを使用します。実際には、これは、定義するカスタム型のほとんどが構造体と列挙型になることを意味します。より詳細な比較については、構造体とクラスの選択 を参照してください。
定義の構文
構造体とクラスは類似の定義構文を持っています。struct キーワードで構造体を導入し、class キーワードでクラスを導入します。どちらも中括弧のペアの中にそれら全体の定義を置きます。
- struct SomeStructure {
- // structure definition goes here
- }
- class SomeClass {
- // class definition goes here
- }
ここで構造体の定義とクラスの定義の例を示します。
- struct Resolution {
- var width = 0
- var height = 0
- }
- class VideoMode {
- var resolution = Resolution()
- var interlaced = false
- var frameRate = 0.0
- var name: String?
- }
上記の例では、ピクセルをベースにしたディスプレイ解像度を記述するために、Resolution という新たな構造体を定義しています。この構造体には、width と height と言う二つの格納されたプロパティがあります。格納されたプロパティは、定数あるいは変数であり、構造体またはクラスの一部として束ねられ、格納されます。この2つのプロパティは、それらに整数 0 の初期値を設定することで、Int 型であることが推測されます。
上記の例では、また、ビデオ表示用の特定のビデオモードを説明するために、VideoMode という新しいクラスも定義しています。このクラスは、4つの格納された変数のプロパティを持っています。最初に、 resolution は、Resolution のプロパティ型を推測する、新しい Resolution の構造体インスタンスで初期化されます。他の3つのプロパティの場合、新しい VideoMode インスタンスは interlaced 設定が false で初期化され("ノンインターレースビデオ" を意味します)、0.0 の再生フレームレート、および name と呼ばれる optional の String 値で初期化されます。それは、optional の型であるため、name プロパティは、自動的に、nil のデフォルト値、または "name のない値" が与えられます。
構造体とクラスのインスタンス
Resolution 構造体の定義と VideoMode クラスの定義は、Resolution や VideoMode がどう見えるかについて説明するだけです。それら自身は特定の resolution または Video Mode を記載していません。これを行なうには、構造体またはクラスのインスタンスを作成する必要があります。
インスタンスを作成するための構文は、構造体とクラスの両方に非常に似ています。
- let someResolution = Resolution()
- let someVideoMode = VideoMode()
構造体とクラスは、両方とも新しいインスタンス用のイニシャライザ構文を使用します。イニシャライザ構文の最も単純な形式は、Resolution() や VideoMode() のように空の括弧が続くクラスまたは構造体の型名を使用します。これは、クラスまたは構造体の新しいインスタンスを作成し、それらのデフォルト値に全てのプロパティは初期化されます。クラスと構造体の初期化は、初期化 で、より詳しく記載されています。
プロパティにアクセス
ドット構文 を使用してインスタンスのプロパティにアクセスできます。ドット構文では、スペースを含まない、ピリオド (.) で区切られた、インスタンス名の直後にプロパティ名を書いて下さい。
- print("The width of someResolution is \(someResolution.width)")
- // prints "The width of someResolution is 0"
この例では、someResolution.width は someResolution の width プロパティを参照し、0 のデフォルトの初期値を返します。
VideoMode の resolution プロパティの width プロパティのように、サブプロパティへと層を下がって行けます。
- print("The width of someVideoMode is \(someVideoMode.resolution.width)")
- // prints "The width of someVideoMode is 0"
また、変数プロパティに新しい値を割り当てるために、ドット構文を使用する事もできます。
- someVideoMode.resolution.width = 1280
- print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
- // prints "The width of someVideoMode is now 1280"
構造体型のためのメンバ化イニシャライザ
すべての構造体は、自動的に生成される メンバ化イニシャライザ を持っており、これは新しい構造体インスタンスのメンバプロパティを初期化するために使用できます。新しいインスタンスのプロパティの初期値は、名前でメンバ化イニシャライザに渡すことができます。
構造体とは異なり、クラス・インスタンスは、デフォルトのメンバ化イニシャライザを受け取りません。イニシャライザは、初期化 で詳しく説明しています。
構造体と列挙型は値型
値型 は、変数または定数に代入される場合、またはそれが関数に渡されるときにその値が コピー される型です。
実際にこれまでの章を通して広範囲に亘って値型を使用してきました。実際に、Swift の基本的な型のすべては、整数、浮動小数点数、ブール値、文字列、配列、および辞書で、それらは値型であり、それは舞台裏では、構造体として実装されます。
すべての構造体と列挙型は Swift では値型です。これは、作成する全ての構造体と列挙型のインスタンスと、それらがプロパティとして持っている全ての値型は、それらがコード内で渡された場合、常にコピーされることを意味します。
配列、辞書、文字列などの標準ライブラリで定義されたコレクションは、最適化を使用してコピーのパフォーマンスコストを削減します。これらのコレクションは、コピーをすぐに作成する代わりに、元のインスタンスと任意のコピーの間に要素が格納されているメモリを共有します。コレクションのコピーの 1 つが変更された場合、要素は変更の直前にコピーされます。あなたのコードで見られる動作は、常にコピーが直ちに起こったかのようになります。
前の例から Resolution の構造体を使用する、この例を考えてみましょう。
- let hd = Resolution(width: 1920, height: 1080)
- var cinema = hd
この例では、hd と言う定数を宣言し、フル HD ビデオの幅と高さ(幅 1920 ピクセル、高さ 1080 ピクセル) で初期化された Resolution インスタンスにそれを設定します。
その後、cinema と言う変数を宣言し、hd の現在値にそれを設定します。Resolution は構造体であるため、既存のインスタンスの コピー が作成され、この新しいコピーは、cinema に代入されます。hd と cinema は今や同じ幅と高さを持っているにもかかわらず、それらは舞台裏では2つの全く異なるインスタンスです。
次に、cinema の width プロパティは、デジタルシネマ投影に使用される、わずかに広い 2K 規格の幅 (幅 2048 ピクセル、高さ 1080 ピクセル) に修正されます。
cinema の width プロパティをチェックすると、それが実際に 2048 に変更されたことがわかります。
- print("cinema is now \(cinema.width) pixels wide")
- // prints "cinema is now 2048 pixels wide"
しかし、元の hd インスタンスの width プロパティは、まだ古い値の 1920 のままです:
- print("hd is still \(hd.width) pixels wide")
- // prints "hd is still 1920 pixels wide"
cinema が hd の現在値を与えられたとき、hd に格納された 値 は、新しい cinema インスタンスにコピーされました。最終結果は、2つの完全に異なるインスタンスとなり、同じ数値を含みます。しかし、それらは別々のインスタンスであるため、cinema の width に 2048 を設定しても、下図に示したように、hd に格納されている width には影響しません。
同じ動作は列挙型にも適用されます。
- enum CompassPoint {
- case north, south, east, west
- mutating func turnNorth() {
- self = .north
- }
- }
- var currentDirection = CompassPoint.west
- let rememberedDirection = currentDirection
- currentDirection.turnNorth()
- print("The current direction is \(currentDirection)")
- print("The remembered direction is \(rememberedDirection)")
- // Prints "The current direction is north"
- // Prints "The remembered direction is west"
rememberedDirection が currentDirection の値を代入された時は、実際にはその値のコピーに設定されます。 currentDirection の値を変更しても、その後それは rememberedDirection に格納されていた元の値のコピーには影響しません。
クラスは参照型
値型とは異なり、変数や定数に代入される時やそれらが関数に渡された時は、参照型 はコピーされ ません。コピーではなく、同じ既存のインスタンスへの参照が代わりに使用されます。
ここで、上記で定義された VideoMode クラスを使用した例を挙げます。
- let tenEighty = VideoMode()
- tenEighty.resolution = hd
- tenEighty.interlaced = true
- tenEighty.name = "1080i"
- tenEighty.frameRate = 25.0
この例では、tenEighty という新しい定数を宣言し、VideoMode クラスの新しいインスタンスを参照するように設定します。ビデオモードは、以前からの 1920 * 1080 の HD 解像度のコピーが代入されています。これは、インターレースと設定され、その名前は "1080i" となり、そのフレームレートは毎秒 25.0 フレームに設定されています。
次に、tenEighty は alsoTenEighty と言う新しい定数に代入され、alsoTenEighty の frmae rate (フレームレート) が変更されます。
- let alsoTenEighty = tenEighty
- alsoTenEighty.frameRate = 30.0
クラスは参照型なので、tenEighty と alsoTenEighty は、実際にはどちらも 同じ VideoMode インスタンスを参照しています。事実上、下図に示したようにそれらは同じ一つのインスタンスの2つの異なる名前に過ぎません。
tenEighty の frameRate プロパティをチェックすると、それは正しく基礎となる VideoMode インスタンスから 30.0 の新しいフレームレートを報告することを示します。
- print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
- // prints "The frameRate property of tenEighty is now 30.0"
この例では、参照型を推論するのがより難しくなる可能性も示しています。tenEighty と alsoTenEighty があなたのプログラムコード内で非常に離れていた場合、ビデオモードが変更されるすべての方法を見つけるのは難しいかもしれません。tenEighty を使用する場合は常に、alsoTenEighty を使用するコードについてもあなたは考えなければなりませんし、逆もまた同様です。対照的に、値型は、同じ値を操作するすべてのコードがソースファイル内で接近しているため、推論するのが容易です。
tenEighty と alsoTenEighty は、変数ではなく、定数 として宣言されていることに注意してください。しかし、まだ tenEighty.frameRate と alsoTenEighty.frameRate を変更できるのは、tenEighty と alsoTenEighty 定数の値それ自体は実際には変更されないからです。tenEighty と alsoTenEighty 自身は VideoMode インスタンスを "格納" せず、代わりに、それらは両方とも舞台裏で VideoMode インスタンスを 参照 しています。変更されるのは、その VideoMode への定数の参照値ではなく、基本となる VideoMode の frameRate プロパティです。
ID 演算子
クラスは参照型であるため、複数の定数や変数は舞台裏で、クラスの同じ一つのインスタンスを参照することが可能です。(構造体と列挙型が定数または変数に割り当てられている場合、または関数に渡される場合、常にコピーされるので、同じことは構造体と列挙型には当てはまりません。)
2つの定数または変数がクラスのインスタンスとまったく同じものを参照しているかどうかを見つけるのは時々役立ちます。これを有効にするのに、Swift は、2つの ID 演算子を提供しています。
- 全く同一である(===)
- 全く同一ではない(!==)
これらの演算子を使用して、2つの定数または変数が同じ一つのインスタンスを参照しているかどうかチェックして下さい。
- if tenEighty === alsoTenEighty {
- print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
- }
- // prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."
"全く同一である" (3つの等号、つまり === によって表される) 事は "等しい” (2つの等号、つまり == によって表される) と同じことを意味するものではないことに注意してください。同一である とは、クラス型の 2 つの定数または変数がまったく同じクラスインスタンスを参照していることを意味します。等しい とは、型の設計者により定義されたように、いくつかの適切な意味で 等しく、2 つのインスタンスが等しいか同等の値であることを意味します。
独自のカスタム構造体とクラスを定義する際には、2つのインスタンスが等しいという資格があるかを決めるのはあなたの責任です。== と != の演算子の独自の実装を定義するプロセスは 等価演算子 に説明されています。
ポインタ
C、C++、または Objective-C の経験をお持ちの場合は、これらの言語では、ポインタ をメモリ内のアドレスを参照するために使用することを知っているでしょう。いくつかの参照型のインスタンスを参照する Swift の定数または変数は、C でのポインタに似ていますが、メモリ内のアドレスへの直接のポインタではなく、また参照を作成していることを示すためにアスタリスク (*) を書く必要もありません。代わりに、これらの参照は、Swift の他の定数または変数のように定義されています。標準ライブラリは、ポインタと直接操作する必要がある場合に使用できるポインタとバッファ型を提供します。手動でメモリー管理 を参照してください。
前:列挙体 次:プロパティ
トップへ
トップへ