75
74

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

The Swift Programming Language - Classes and Structures(クラスと構造体)をまとめる

Last updated at Posted at 2014-06-07

The Swift Programming Language をまとめるトップ

Classes and Structures(クラスと構造体)

クラスにはプロパティ、メソッドを組み込む事ができる

クラスや構造体をつくる時、インターフェースや実装コードを含むファイルを必要としない
ファイルを分割しないでよくて、クラスや構造体は一つのファイルに書くことができる
外部から参照できるインターフェース自動的に使えるようになる

クラスと構造体の共通点

  • 値を保持する事ができるプロパティの定義ができる
  • メソッドを定義して機能を提供できる
  • 添字を定義してそれらの値にアクセスできる
  • イニシャライザーを定義してその中で初期設定ができる
  • 実装を継承して機能性を高めることができる
  • プロトコルに従って基本的な機能を提供することができる

詳細は Properties, Methods, Subscripts, Initialization, Extensions, and Protocols にて

クラスのみにある特徴
継承ができる
型のキャスティングができる
ランタイム中に型の解釈ができる
破棄してリソースをフリーにする事ができる
リファレンスをカウントすることで、複数のリファレンスで同一クラスを利用できる

詳細は Inheritance, Type Casting, Initialization, and Automatic Reference Counting にて

Note
構造体を定義する時は、毎回複写される

Definition Syntax(定義の方法)

{} を使ってクラスも構造体も同じ様に定義できる


    class SomeClass {
    // class の定義
    }
    struct SomeStructure {
    // structure の定義
    }

Note
クラスや構造体を定義する時は、新しくSwiftの型を指定する事なので、必ず最初の文字を大文字にする
最初の文字をプロパティやメソッドは小文字にする

ストラクチャとクラスの定義のサンプル

struct Resolution {
    var width = 0
    var height = 0
}

class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

まず、 Resolution という構造体を定義している
ピクセルベースのディスプレイの解像度をあらわしている
widthheight 2つの プロパティ を保持している
プロパティには定数(let)や変数(var)を宣言でき、クラスや構造体にまとめることができる
今回は値が変わる場合があるので変数を宣言する
そして、0を代入する
事の時、推論で Int 型で定義される

次に、 VideMode がクラスとして定義されている
ビデオのディスプレイモードをあらわしている
このクラスは解像度、インターレース、フレームレート、名前4つの値を定義したプロパティを保持している
どれも、型を推論してる
new VideoMode をすることで、name 以外は初期値でイニシャライズされる
name は String? オプショナルで定義されているので、nil が自動でセットされる

Class and Structure Instances(クラスと構造体のインスタンス)

インスタンスを以下のように定義する

let someResolution = Resolution()
let someVideoMode = VideoMode()

引数の指定がない空の括弧()を名前の後に続けて書いてインスタンスを初期化する

Accessing Properties(プロパティアクセス)

プロパティには.ドットシンタックスで接続できる

println("someResolution の幅は \(someResolution.width)")
// prints "someResolution の幅は 0"

この例だと、someResolution.width は someResolution のプロパティを参照して初期値の 0 を返す

VideMode のプロパティに設定された resolution 経由でサブプロパティとしてアクセスする事もできる

println("someVideoMode の幅は \(someVideoMode.resolution.width)")
// prints "someVideoMode の幅は 0"

新しい値の代入もできる

someVideoMode.resolution.width = 1280
println("someVideoMode の幅は \(someVideoMode.resolution.width)")
// prints "someVideoMode の幅は 1280"

Memberwise Initializers for Structure Types(構造体のメンバープロパティごとの代入と初期化)

あらゆる構造体は、新しい構造体のインスタンスとして、自動でメンバープロパティを初期化する

プロパティの名前を指定して初期化するときに値をしていすることができる

let vga = Resolution(width: 640, height: 480)

クラスは構造体と違って上記の様に初期値を受け取る事ができない

Structures and Enumerations Are Value Types(構造体と列挙型(enum)値型)

値の型は変数や定数が代入された時か引数として渡されたときに複写される
全ての基本の型は構造体として存在している

全ての構造体や列挙型(enum)は値の型であり、つまり、全ての構造体や列挙型(enum)のインスタンスを生成するときには、そのプロパティは複写されるしくみになっている

以下の例をみてみる
Resultion 構造対は上記の例の続き

let hd = Resolution(width: 1920, height: 1080)
var cinema = hd

hd を定数として宣言して Resultion インスタンスを新しい値 widht:1920 height:1080 をもって定義

次に、 cinema を変数として hd で定義、cinmeahd のコピーとしてつくられる
cinemahd のプロパティの値は同じだが、全く異なるインスタンス

次に、2048 を cinema の幅に指定してみる

cinema.width = 2048

それぞれのプロパティの値を覗いてみる

println("cinema is now \(cinema.width) pixels wide")
// prints "cinema is now 2048 pixels wide"

println("hd is still \(hd.width) pixels wide")
// prints "hd is still 1920 pixels wide"

hd のプロパティと cinema のプロパティの width が違う事がわかる

enum 型も同じ動作をする

enum CompassPoint {
    case North, South, East, West
}
var currentDirection = CompassPoint.West
let rememberedDirection = currentDirection
currentDirection = .East
if rememberedDirection == .West {
	println("The remembered direction is still .West")
}
// prints "The remembered direction is still .West"

Classes Are Reference Types(クラスは参照型)

クラスは構造体やenumとは違い、リファレンスを返す

videoMode を使ってみる例をみてみる

let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

定数で tenEighty という名前で VideoModeクラスのインスタンスを宣言し定義をしている

次に、tenEnghty を使って定数 alsoTenEight を新しく定義して、frameFrame を編集してみる

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

tenEighty と alsoTenEighty は同じものを参照しているので、tenEighty の tenEighty も同時に変わる

println("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// prints "The frameRate property of tenEighty is now 30.0"

Note
定数で宣言してもプロパティの値を変更することができる実際に変更しているのはクラス内のメンバープロパティで、インスタンス自体は更新している値はその定数のリファレンスではない

Identity Operators(同一演算)

同じインスタンスを参照しているか確認することができる

  • 同一の場合 (===)
  • 同一で無い場合 (!==)
if tenEighty === alsoTenEighty {
    println("tenEighty and alsoTenEighty refer to the same Resolution instance.")
}
// prints "tenEighty and alsoTenEighty refer to the same Resolution instance."

NOTE
同等(==)の場合は値を比較するのとは違う
あくまでも、同じインスタンスを参照しているかをを確認する時に用いる

Pointers(ポインター)

ポインターはメモリー上のアドレスを参照する
C言語のポインターと似た感じで定数と変数は振る舞うが、直接のポインターではなない。
(*)アスタリスクを使わないでリファレンスをつくる事ができる
Swift では定数と変数としてリファレンスが定義される

Choosing Between Classes and Structures(クラスか構造体か?)

データタイプを定義する時にクラスと構造体両方使える
構造体は値を返し、インスタンスはリファレンスを返し使い分けが必要

構造体を選ぶ場合の一般的なルール

  • 基本的な目的はお互い関係の単純なデータ値をまとめるためのもの
  • まとまった値が複写される事を期待される理由があるとき
  • 複写が期待されている構造体が保持されているプロパティ
  • 他の現存す型の動作やプロフパティを継承する必要がない

上記以外は、class を使うほうがよい
データモデルはクラスをつかったほうがよい

Assignment and Copy Behavior for Collection Types(コレクション型の複写について)

Array と Dictionary はクラスとして実装されているので、コピーされない
リファレンスが渡される

Assignment and Copy Behavior for Dictionaries(連想配列の複写について)

関数やメソッドの引数として渡された時は、値が複写される
また、変数タイプで宣言されたときもコピーされる

var ages = ["Peter": 23, "Wei": 35, "Anish": 65, "Katya": 19]
var copiedAges = ages
copiedAges["Peter"] = 24
println(ages["Peter"])
// prints "23"

もとの値と更新した値が異なる

Assignment and Copy Behavior for Arrays(配列の複写について)

配列もコピーじゃなくて同じ配列の要素を共有している状態になる
配列の要素の長さが変わった時にコピーされる

例えば

var a = [1, 2, 3]
var b = a
var c = a

配列変数を定義する
最初のindexは全て 1

println(a[0])
// 1
println(b[0])
// 1
println(c[0])
// 1

42をa[0]に代入すると他のも同じように変わる
同じものを参照している

a[0] = 42
println(a[0])
// 42
println(b[0])
// 42
println(c[0])
// 42

新しい値を追加するとコピーを作成し別のものになる

a.append(4)
a[0] = 777
println(a[0])
// 777
println(b[0])
// 42
println(c[0])
// 42

Ensuring That an Array Is Unique(配列のユニーク化)

unshare()

unshareメソッドを呼ぶ事で、共有していいた配列の要素から切り離され新しく独立した配列データになる
ちなみに定数はunshareできない

上記の例の続きで

b.unshare()

配列bがunshareを読んで、異なるデータになる
a, b, c 全て違う配列になった

b[0] = -105
println(a[0])
// 777
println(b[0])
// -105
println(c[0])
// 42

===!== を使って同じものを参照しているか確認できる

if b === c {
	println("b and c は同じ配列の要素をシェアーしている")
} else {
    println("b and c は新しい配列の要素を参照している")
}
// prints "b and c は新しい配列の要素を参照している"

以下の様にも書ける

if b[0...1] === b[0...1] {
	println("These two subarrays share the same elements.")
} else {
	println("These two subarrays do not share the same elements.")
}
// prints "These two subarrays share the same elements."

Forcing a Copy of an Array(配列の強制複写)

copy

明示的にコピーする場合、copyメソッドを使う
浅いコピーで新しい配列をつくる

名前の配列をつくって、copiedNames に明示的にコピーする

var names = ["Mohsen", "Hilary", "Justyn", "Amy", "Rich", "Graham", "Vic"]
var copiedNames = names.copy()

copiedNames[0]  "Mo" を代入してもオリジナルは変化なし
```swift
copiedNames[0] = "Mo"
println(names[0])
// prints "Mohsen"

NOTE
unshareは必要に応じてコピーするが、copyは必ず呼ばれたときに複写する

75
74
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
75
74

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?