#author("2016-05-16T19:39:17+00:00","default:nitta","nitta") [[Swift]] *Swift/クラス [#n73f083c] クラスのインスタンスは参照型である。 すなわち、変数に代入したりメソッドの引数に渡す時に、コピーが作られるのではなく、同一のインスタンスへの参照が渡される。 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] ルートクラスは 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]