- 追加された行はこの色です。
- 削除された行はこの色です。
#author("2016-05-16T19:39:17+00:00","default:nitta","nitta")
#author("2016-05-20T11:04:44+00:00","default:nitta","nitta")
[[Swift]]
*Swift/クラス [#n73f083c]
**概要 [#deccc028]
クラスのインスタンスは参照型である。
すなわち、変数に代入したりメソッドの引数に渡す時に、コピーが作られるのではなく、同一のインスタンスへの参照が渡される。
class 型名 [: スーパークラス] {
[変数/定数定義]
[イニシャライザ定義]
[メソッド定義]
[その他の定義]
}
クラスでは memberwise initializer は利用できない。
内部の変数の名前を指定して初期値を与えるのは、オブジェクト指向の情報隠蔽
(encapsulation) を台無しにしてしまうので。
クラス定義にイニシャライザがない場合、「クラス名()」という記法で default initializer
でインスタンスを生成できるが、すべてのインスタンスプロパティに初期値が設定されている必要がある。
class Time {
var hour = 0, min = 0
init(hour:Int, min:Int) {
self.hour = hour; self.min = min
}
func add(min:Int) {
let m = self.min + min
if m = 60 {
self.min = m % 60
let t = self.hour + m / 60
self.hour = t % 24
} else {
self.min = m
}
}
}
let t1 = Time(hour:13, min:20)
let t2 = t1
print(t1.toString()) // 13:20 と表示される
ti.inc()
print(t2.toString()) // 13:21 と表示される
構造体の定義ではメンバ変数の値を変更するメソッドには mutating という修飾子をつけたが、
クラスの定義では必要ない。
クラスのインスタンスは、定数と変数のどちらに格納されていても、格納型プロパティの変更が可能である。
LEFT:
||クラス|構造体|列挙型|
|参照型/値型|参照型|値型|値型|
|変更に関する制約|なし|(*1)|(*2)|
|インスタンスプロパティ|格納型/計算型|格納型/計算型|-/計算型|
|タイププロパティ|格納型/計算型|格納型/計算型|格納型/計算型|
|継承|◯|-|-|
|プロトコル継承|◯|◯|◯|
|memberwise initializer|-|◯|-|
-(*1) --- インスタンスの格納型プロパティの値を変更するメソッドに mutating 指定が必要。また、定数に格納されたインスタンスはプロパティの値が変更できない。
-(*2) selfの値を変更するメソッドにmutating指定が必要。
** クラスの継承 [#zce9016e]
*** クラスの継承 [#zce9016e]
ルートクラスは NSObject。
継承において、スーパークラスのメソッドを上書き(override)することができるが、
メソッドの先頭部分に override という修飾語を記述する必要がある。
class クラス名 : スーパークラス名, プロトコル1, プロトコル2, ...
class Time12 : Time, CustomStringConvertible {
var pm: Bool // 午後ならtrue
init(hour:Int, min:Int, pm:Bool) {
self.pm = pm
super.init(hour:hour, min:min)
}
override convenience init(hour:Int, min:Int) { // 24時制
let isPm = hour >= 12
self.init(hour: isPm ? hour - 12 : hour, min: min, pm: isPm)
}
override func add(min:Int) {
super.add(min)
while hour >= 12 {
hour -= 12
pm = !pm
}
}
var description: String {
return toString() + " " + (pm ? "PM" : "AM")
}
}
クラスの継承を利用すると、スーパークラスに対して定義されたメソッドが、サブクラスに対しても同様に動作する。
実行時に動的に動作を決めることができる(dynamic binding)。
var array = [Time]() // Time型の配列
array.append(Time(hour:13, min:10))
array.append(Time12(hour:13, min:20))
array.append(Time12(hour:1, min:30, pm:true))
for t in array {
if t is Time12 { // is は動的にインスタンスの型を調べる演算子
print(t) // Time12型ならそのまま表示
} else {
print(">", t.toString()) // 先頭の">"の後に表示する。
}
}
if let u = array[2] as? Time12 { // オプショナル束縛構文
print(u.pm ? "PM" : "AM")
}
インスタンスをキャスト(cast)する演算子として as, as?, as! がある。
クラスメソッドや、クラスプロパティでは class という修飾語を記述する。
タイプメソッドやタイププロパエィは上書きできないが、
クラスメソッドやクラスプロパティはサブクラスで定義を上書きできる。
クラスプロパティは計算型プロパティに限定されている。
clas A : CustomStringConvertible {
static var className : String { return "A" } // 計算型タイププロパティ
static var total: Int = 0 // 格納型タイププロパティ
class var level: Int = { return 1 } // 計算型クラスプロパティ
static func point() -> Int { return 1000 } // タイプメソッド
class func say() -> String { return "Ah." } // クラスメソッド
init() {) { ++A.total }
var description: String {
return "\(A.toal): \(A.clasName), Level=\(A.level), \(A.point())pt, \(A.say())"
}
}
class B : A {
override init() { super.ini(); ++B.total }
override var description : String {
return "\(B.toal): \(B.className), level=\(B.level), \(B.point())pt, \(B.say())"
}
}
let a = A()
print(a) // "1: A, Level=1, 1000pt, ah."と表示される
let b = B()
print(b) // "3: A, Level=1, 1000pt, ah."と表示される
クラスBの中で、クラスAのタイププロパティやクラスプロパティを使うことができる。
class B : A {
// override static var className: String { return "B" } // 定義不可
// static var total:Int = 0 // 定義不可
override class var level : Int { return 2 }
// override static func point() -> Int { return 2000 } // 定義不可
override class func day() -> String { return "Boo" }
override init() { super.ini(); ++B.total }
override var description : String {
return "\(B.toal): \(B.className), level=\(B.level), \(B.point())pt, \(B.say())"
}
}
let a = A()
print(a) // "1: A, Level=1, 1000pt, ah."と表示される
let b = B()
print(b) // "3: A, Level=2, 1000pt, Boo"と表示される
**継承とメソッド呼び出し [#me0bbc73]
***継承とメソッド呼び出し [#me0bbc73]
継承関係にあるクラスA, Bはそれぞれ show メソッドを持っている。
class A {
func show() { print("A") }
func who() { show() }
}
class B: A {
override func show() { print("B") }
}
var a = A()
var b = B()
a.who() // Aと表示される
b.who() // Bと表示される
メソッドwhoにおけるshowの呼び出しは, self.show() と記述されているのと同じなので、
実際に実行されているインスタンスのメソッド定義が呼び出される。
クラスメソッドについても同様である。
class A {
class func show() { print("A") }
class func who() { show() }
}
class B: A {
override class func show() { print("B") }
}
var a = A()
var b = B()
a.who() // Aと表示される
b.who() // Bと表示される
**イニシャライザ [#u2c116b3]
-designated initializer --- ベースクラスでは、そのイニシャライザを使うだけでインスタンスの初期化がすべて完了できるもの。サブクラスでは、直接のスーパークラスのdesignated initializer以外の助けを借りずに初期化作業を行うもの。
-convenience initializer --- 他のイニシャライザを呼び出してその助けを借りる(Swiftではこれを delegate と呼ぶ)ことで初期化を行うもの。同じクラスのdesignated initializerを呼び出すのはよいが、スーパークラスのイニシャライザを呼び出してはいけない。定義のときに convenience というキーワードを付ける。
スーパークラスのdesignated initializerと同じイニシャライザをサブクラスで定義した場合、
override というキーワードをつける必要がある。
designated initializer
init([仮引数: 型]... ) { 文 ... }
convenience initializer
convenience init([仮引数: 型] ...) { 文 ... }
初期化の手順と守るべきルール
+サブクラスのイニシャライザが呼び出される。もしconvenience initializerの場合はインスタンスの変数や定数に値を設定することはまだできない。必要な前処理があれば行った上でdesignated initializerを呼び出す。
+そのサブクラスで追加された変数や定数の初期化を行う。初期値が宣言されていれば、それが格納される。代入文でも設定できる。
+サブクラスから