from Minority

〜log

関数と変数をまとめる[クラス]

クラスの定義

  • クラス定義
class Counter {
  var count = 0

  func increment() -> Int {
    ret
  }
}
対象 表記
クラス アッパーキャメル
プロパティ ロウワーキャメル
メソッド ロウワーキャメル

スネークケース「_」は非推奨


インスタンスの作成

var cnt1 = Counter()
var cnt2 = Counter()

インスタンスを定数に代入した場合、以降のインスタンスの代入は不可
ただし、インスタンスのプロパティは変更可


プロパティ

  • プロパティへのアクセス
var c = Count()
c.count // 0
c.increment()
c.count // 1


ストアドプロパティ クラスの持つ値を格納するプロパティのこと
letキーワード付きは変更不可


レイジーストアドプロパティ
「lazy」キーワードがついたプロパティ
クラスに必要な情報が不確定の際に利用し、実際の使用まで初期化させない


コンピューテッドプロパティ

  • コンピューテッドプロパティの定義
var プロパティ名 : 型 {
  // 必須
  get {
    ゲッター処理
  }

  // 必須ではない
  set(セットする値){
    セッター処理
  }
}

計算によって値を返すプロパティ
ゲッター関数で値を返す


  • コンピューテッドプロパティの例
class Rectangle {
  // ストアドプロパティ
  var origin = (x: 0, y: 0)
  var size = (width: 0, height: 0)

  // コンピューテッドプロパティ
  var center : (x: Int, y: Int) {
    // ゲッタ
    get {
      let centerX = origin.x + (size.width / 2)
      let centerY = origin.y + (size.height / 2)
      return (x: centerX, y: centerY)
    }

    // セッタ
    set(newCenter) {
      origin.x = newCenter.x - (size.width / 2)
      origin.y = newCenter.y - (size.height / 2)
    }
  }
}

var rect = Rectangle()
rect.origin = (x: 100, y: 100)
rect.size = (width: 50, height: 50)
rect.center // (.0 125, .1 125)

rect.center = (x: 200, y: 200) 
rect.center // (.0 200, .1 200)


  • セッタのパラメタの省略
set {
  origin.x = newCenter.x - (size.width / 2)
  origin.y = newCenter.y - (size.height / 2)
}


  • 読み取り専用コンピューテッドプロパティ
class Rectangle {
  // ストアドプロパティ
  var origin = (x: 0, y: 0)
  var size = (width: 0, height: 0)

  // コンピューテッドプロパティ
  var center : (x: Int, y: Int) {
      let centerX = origin.x + (size.width / 2)
      let centerY = origin.y + (size.height / 2)
      return (x: centerX, y: centerY)
  }
}


  • タイププロパティ(static)
class Foo {
  class var bar: Int {
    return 100
  }
}

Foo.bar // 100

インスタンスには関連付いていない
構造体や列挙型を総称して「 タイプ
コンピューテッドプロパティとしてしか使用できない
classキーワードとvarキーワードで表記


  • 特殊なプロパティ / すべてのインスタンスは「 self 」という特別なプロパティを持つ
class Rectangle {
  var x = 0, y = 0
  var width = 0, height = 0

  // クラスプロパティと同一名の引数
  func center(x: Int, y: Int) -> (x: Int, y: Int) { 
    // selfキーワードを使用して自クラスのプロパティとして宣言
    let centerX = self.x + (width / 2)
    let centerY = self.y + (height / 2)
    return (x: centerX, y: centerY)
  }
}


  • プロパティオブザーバ / プロパティが設定されるたびに呼び出される関数
class Wallet {
  var money: Int = 0 {
    // 設定する前に呼び出される
    willSet {
      println("財布の中身が\(newValue)円になりました")
    }

    // 設定した後に呼び出される
    didSet {
      if oldValue < money {
        println("\(meney - oldValue)円増えました")
      } else {
        println("\(oldValue - meney)円減りました")
      }
    }
  }
}

var w = Wallet()
w.meney = 100
// 財布の中身が100円になりました
// 100円増えました
w.meney = 150
// 財布の中身が150円になりました
// 50円増えました

「willSet」「didSet」は片方のみ定義可。最初の初期化では呼び出されない
「newValue」「oldValue」という名前でそれぞれの定数にアクセス可


  • 通常の変数への適用 / コンピューテッドプロパティ・プロパティオブサーバは一般変数にも適用可
var absolute = Int = 0 {
  didSet {
    if absolute < 0 {
      absolute *= -1
    }
  }
}

absolute = -10
absolute // 10


メソッド / インスタンスメソッド・タイプメソッド

class Counter {
  var count = 0

  func increment() -> Int {
    return ++ count
  }

  func incrementBy(amount: Int) -> Int {
    count += amount
    return count
  }

  func incrementBy(amount: Int,  times: Int) -> Int {
    count += amount * times
    return count
  }
}

var c = Counter()
c.count // 0
c.increment()
c.increment()
c.count // 2
c.incrementBy(3)
c.count // 5

インスタンスメソッドの実行は「.(半角ドット)」構文


var c = Counter()
c.count // 0
// 外部名が指定されていなくても内部名を外部名として指定可
c.incrementBy(5, times: 3) 
c.count // 15

メソッド名の最後には、最初の引数を説明する単語を置くという命名ルール


  • パラメタを指定しない形の新スタンスメソッド
class Counter {
  var count = 0
  
  func incrementBy(amount: Int, _ times: Int) -> Int {
    count += amount * times
    return count
  }
}

var c = Counter()
c.count // 0
c.incrementBy(5, 3)
c.count // 15


class Foo {
  class func bar() {
    // タイプメソッド処理
  }
}


クラスの継承

  • クラスの継承の宣言
class クラス名 : スーパークラス名 {
  クラス定義
}


  • ベースクラスの定義と継承の例
class Instrument {
  var rangeOfOctaves = 1
  var description: String {
    return "\(rangeOfOctaves)オクターブの音がでます"
  }

 func play(){
  }
}

class Guitar: Instrument {
  var numberOfString = 6
}

var g = Guitar()
g.rangeOfOctaves = 4
g.numberOfString // 6


class Trumpet: Instrument {
  override func play() {
    println("ぷぷー!")
  }
}


  • プロパティのオーバーライド
class Trumpet: Instrument {
  var lowestNote = "C4"
  // コンピューテッドプロパティdescriptionのゲッタをオーバーライド
  override var description: String {
    return "\(lowestNote)から" + super.description
  }

  override func play(){
    println("ぷぷー!")
  }
}

セッタをオーバーライドする場合には、ゲッタもオーバーライドが必要


初期化

  • イニシャライザ
class Distance {
  var distanceInKm: Double

  init() {
    distanceInKm = 0
  }
}

ストアドプロパティはインスタンスの初期化で値の設定が必要
定義時設定か、初期化にて設定。

letはイニシャライザ初期化可。以後は変更不可。


  • Optional型のプロパティの初期化
class Article {
  var title: String?
}

var a = Article()
a.title // nilで初期化される


  • 定数プロパティ
class Article {
  var title: String?
  let category: String = "default"

  init() {
  }

 init(category: String){
    self.category = category
  }
}

var a1 = Article()
a1.category // default定義

var a2 = Aeticle(category: "Blog") // イニシャライザで設定
a2.category // Blog


  • 複数のイニシャライザ
class Distance {
  var distanceInKm: Double

  init(){
    distanceInKm = 0
  }

  init(fromMeter meter: Double){
    distanceInKm = meter / 1000
  }

  init(fromMile: Double){
    distanceInKm = fromMile * 1.609344
  }
}

var d1 = Distance(fromMeter: 10)
d1.distanceInKm  // 0.01

var d2 = Distance(fromMile: 10)
d2.distanceInKm // 16.09344

外部名を指定。外部名が定義されていない場合は内部名。
『_』を指定して、外部名を省略可。


  • デフォルトイニシャライザ

ベースのクラスでプロパティが指定され、イニシャライザが定義されていない場合、 自動的に定義される。引数なし。


  • 失敗可能なイニシャライザ / init?キーワード
let urlOptional = NSURL(string: "http://www.google.com")

// urlはOptional型の変数
if let url = urlOptional {
  // 成功
} else {
  // 失敗ではnilが返る
}


  • クラスの継承とイニシャライザ

サブクラスの初期化はスーパークラスも含めた初期化が必要。
Swiftは正しく初期化されるため、2種類のイニシャライザを定義している。

1 指定イニシャライザ
2 コンビニエンスイニシャライザ

  • 指定イニシャライザ
init(パラメタ) {
  // 初期化処理
}

// 自身がサブクラスなら、スーパークラスのイニシャライザを実行する


  • コンビニエンスイニシャライザ
convenience init(パラメタ) {
  // 初期化処理  
}
  • イニシャライザのルール

    • サブクラスの指定イニシャライザでは自身で定義したストアドプロパティを初期化してからスーパークラスの指定イニシャライザを実行する
    • サブクラスの指定イニシャライザはスーパークラスから継承したプロパティに値を設定する前にスーパークラスの指定イニシャライザを実行する
    • コンビニエンスイニシャライザはプロパティに値を設定する前に他のイニシャライザを実行する
    • イニシャライザは自身で定義したストアドプロパティの初期化およびスーパークラスの指定イニシャライザの実行が完了するまで他のインスタンスメソッドを実行したり、インスタンスプロパティの値やselfを参照したりすることはできない
  • 上記を考慮した例

class Instrument {
  var rangeOfOctaves = 0
  var description: String {
    return "\(rangeOfOctaves)オクターブの音がでます"
  }

  func play() {
  }
}

class Trumpet: Instrument {
  var lowestNote: String

  override init (){
    lowestNote = "C4"
    super.init()
    rangeOfOctaves = 1
  }
}

var t = Trmpet()
t.description
// 1オクターブの音がでます


  • イニシャライザの継承 サブクラスはスーパークラスのイニシャライザを通常は継承しないが、特定の条件を満たすとスーパークラスのイニシャライザが継承される。 1 サブクラスに指定イニシャライザを定義していない時。
    2 サブクラスにスーパークラスが持つ全てのイニシャライザが定義されている時。この場合、スーパークラスのコンビニエンスイニシャライザが継承される。

  • 関数やクロージャを使用した初期化

class Dice {
  var spot: UInt32 {
    return arc4random_uniform(6)
  }()
}


デイニシャライザ / インスタンスが破棄される前に実行される処理

  • デイニシャライザの定義
deinit {
  // 処理
}

プログラム内で明示的に実行できない・・・。


型キャスト / 『as』演算子・『is』演算子

class MediaItem {
  var title: String
  init(title: String) {
    self.title = title
  }
}

class CD: MediaItem {
  var artist: String
  init(title: String, artist: String) {
    self.artist = artist
    super.init(title: title)
  }
}

class DVD: MediaItem {
  var director: String
  init(title: String, director: String){
    self.director = director
    super.init(title: title)
  }
}

let mediaLibrary = [
  CD(title: "Music", artist: "Madonna"),
  CD(title: "時間の翼", artist: "ZARD"),
  DVD(title: "The Devil Wears Prada", director: "David Frankel"),
  DVD(title: "天空の城ラピュタ", director: "宮崎駿"),
]
// 型推測でMediaItem型の配列となる


for item in mediaLibrary {
  if item is CD {
     println("CD: \(item.title)")
  } else if item is DVD {
     println("DVD: \(item.title)")
  }
}


  • ダウンキャスト
for item in mediaLibrary {
  if let cd = item ~~as?~~as! CD {
    println("CD: '\(cd.title)' artist: '\(cd.artist)'")
  } else if let dve = item ~~as?~~as! DVD {
    println("DVD: '\(dvd.title)' director: '\(dvd.director)'")
  }
}

解説:『as?』『as!』を使用して、Optional型としてダウンキャストの結果を取得し、 Optional Bindingを使用してOptional型をアンラップし元の型を得ている。


  • AnyObject型とAny型

AnyObject

すべてのクラスを含む型として使用可能

Any

Int型やString型などの値や構造体、クラスなど関数型以外のすべて


  • 型チェックとダウンキャストの応用
var things = [Any]()
things = [
  0,
  0.0,
  10,
  "swift",
  (10, 10),
  CD(title: "Music", artist: "Madonna"),
]

for thing in things {
  switch thing {
    case 0 as Int:
      println("Intとしての0")

    case 0 as Double:
      println("Doubleとしての0")

   case let someInt as Int:
      println("何らかのInt: \(someInt)")

   case let someString as String:
      println("何らかのString: \(someString)")

   case let cd as CD:
      println("DC: \(cd.title)")

   default:
      println("それ以外のクラス")

  }
}


プロトコル / デザインパターンの一種

メソッド・プロパティの設計のみを定義

protocol SomeProtocol {
  var someProperty: Int { get set } // セッタ・ゲッタを持つプロパティ
  var someReadOnlyProperty: Int { get } // ゲッタのみのプロパティ

  class func someTypeMethod() // タイプメソッド
  func someInstanceMethod(str: String) -> String // インスタンスメソッド
}


class SomeClass: SomeProtocol {
  var someProperty = 10
  var someReadOnlyProperty = 30

  clss func someTypeMethod() {
    println("type method")
  }

  func someInstanceMethod(str: String) -> String {
    return "instance method"
  }
}

お疲れ様でした。