開発メモ

開発関係のメモをいろいろと。たぶん。

The Swift Programming Languageのメモ(その9)

はじめに

これは、『The Swift Programming Language』を読んだ時の個人的なメモです。なので、引用はすべて『The Swift Programming Language』から。

iTunes - ブック - Apple Inc.「The Swift Programming Language」
https://itunes.apple.com/jp/book/swift-programming-language/id881256329?mt=11

その8はこちら。

The Swift Programming Languageのメモ(その8) - 開発メモ
http://seeku.hateblo.jp/entry/2014/06/12/191018


Optional Chaining

  • オプション連鎖は、nilになる可能性があるプロパティやメソッドや添字アクセスを呼び出す方法だよ
  • オプション連鎖を使うと、連結したオプション変数を優雅に取り扱うことが出来るよ

Optional Chaining as an Alternative to Forced Unwrapping

  • オプション変数の後ろに『?』を書くことで、オプション連鎖を使えるよ
  • オプション変数の後ろに『!』を書くのと似てるけど、こっちはnilでもランタイムエラーにならないよ
  • オプション連鎖の戻り値は、必ずオプション変数になるよ
    戻り値をチェックすれば、オプション連鎖の呼び出しに成功したのかどうかを判定できるよ。

  • 具体的にはこんな感じになるよ

if let roomCount = john.residence?.numberOfRooms {
    println("John's residence has \(roomCount) room(s).")
} else {
    println("Unable to retrieve the number of rooms.")
}
// prints "Unable to retrieve the number of rooms.
  • この場合、numberOfRoomsがオプション変数じゃ無くても、戻り値はオプション変数になることに注意が必要だよ

Calling Methods Through Optional Chaining

  • オプション連鎖で戻り値の無いメソッドを呼び出した場合、戻り値は『Void?』になるよ
    つまり、Void型のオプション変数だよ。

  • これをチェックすることで、メソッドを呼び出すことが出来たかどうかわかるよ

Calling Subscripts Through Optional Chaining

  • オプション変数に添字アクセスする場合、『?』は変数名の直後に書くよ
if let firstRoomName = john.residence?[0].name {
    println("The first room name is \(firstRoomName).")
} else {
    println("Unable to retrieve the first room name.")
}
// prints "Unable to retrieve the first room name."

Linking Multiple Levels of Chaining

  • オプション連鎖がどのレベルまで成功したかはチェック出来ないよ

Chaining on Methods With Optional Return Values

  • オプション連鎖にオプション変数を返すメソッドを挟む場合、『?』はメソッドの後ろに書くよ
if let upper = john.residence?.address?.buildingIdentifier()?.uppercaseString {
    println("John's uppercase building identifier is \(upper).")
}
// prints "John's uppercase building identifier is THE LARCHES.

Type Casting

  • 型キャストは、インスタンスの型をチェックしたり変更したりする仕組みだよ
  • Swiftでは、型キャストに『is』や『as』演算子を使うよ

Checking Type

var movieCount = 0
var songCount = 0
 
for item in library {
    if item is Movie {
        ++movieCount
    } else if item is Song {
        ++songCount
    }
}

Downcasting

  • インスタンスの型を確信してる場合、『as』を使うとダウンキャスト出来るよ
    この場合、ダウンキャストって言うのはスーパークラスをサブクラスに変換することだよ。

  • 『as?』を使うとダウンキャストの結果がオプション変数になるよ
    もちろん、変換に失敗すると結果はnilになるよ。

  • 『as』を使うと強制的にダウンキャスト出来るよ
    もちろん、変換に失敗するとランタイムエラーになるよ。

Type Casting for Any and AnyObject

  • Swiftは型を特定できない場合のために、2つの特別なエイリアスを用意してるよ

    • 『AnyObject』はすべてのクラスのインスタンスを表すよ
    • 『Any』は関数型以外のすべての型を表すよ
      メモ: Anyの解釈はこれであってる? 要確認
  • NOTE: AnyとAnyObjectは互換性が必要な場合のためだけに用意してあるよ。出来れば、型をはっきりさせた方がいいに決まってるよ。

AnyObject

  • CocoaAPIを扱う場合、[AnyObject]を受け取ることがよくあるよ
  • こんな時は、それぞれのアイテムを『as』でダウンキャストして使えばいいよ
for object in someObjects {
    let movie = object as Movie
    println("Movie: '\(movie.name)', dir. \(movie.director)")
}
  • 元々の配列をダウンキャストする方法もあるよ
for movie in someObjects as [Movie] {
    println("Movie: '\(movie.name)', dir. \(movie.director)")
}

Any

  • Anyはクラスのオブジェクトも含めて、複数の型を表すことが出来るよ
var things = [Any]()
 
things.append(0)
things.append(0.0)
things.append(42)
things.append(3.14159)
things.append("hello")
things.append((3.0, 5.0))
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
  • この例だと、IntやDoubleやStringやタプルやMovie型をAnyにしてるよ
  • 『is』や『as』はswitch文の条件で型を特定するために使えるよ
    この場合、元の値で使えるのは『Any』と『AnyObject』だけだよ。
for thing in things {
    switch thing {
    case 0 as Int:
        println("zero as an Int")
    case 0 as Double:
        println("zero as a Double")
    case let someInt as Int:
      println("an integer value of \(someInt)")
    case let someDouble as Double where someDouble > 0:
        println("a positive double value of \(someDouble)")
    case is Double:
        println("some other double value that I don't want to print")
    case let someString as String:
        println("a string value of \"\(someString)\"")
    case let (x, y) as (Double, Double):
        println("an (x, y) point at \(x), \(y)")
    case let movie as Movie:
        println("a movie called '\(movie.name)', dir. \(movie.director)")
    default:
        println("something else")
    }
}
  • NOTE: switch文の条件には『is』や『as』を使ってるけど、この場合は安全だよ。

Nested Types

  • 列挙型は、特定のクラスや構造体のために作られることがよくあるよ
  • この場合、列挙型がクラスや構造体の中で定義できると便利だよね?
  • Swiftでは、型のネストが出来るよ
    型ネストでは列挙型とクラスと構造体を、他の型の中に定義できるよ。

  • 型ネストは、型を定義するときにその中に定義を書くだけだよ

  • 何段階でもネストできるよ

Referring to Nested Types

  • ネストした型を外から使うには、型名を続けて書けばいいよ
let heartsSymbol = BlackjackCard.Suit.Hearts.toRaw()
// heartsSymbol is "♡”

Extensions

  • エクステンションは既存のクラスや構造体や列挙型に機能を追加する方法だよ
  • 元のソースコードがなくても追加できるよ
  • Objective-Cのカテゴリに似てるけど、Swiftのエクステンションは名前を持たないよ

  • Swiftのエクステンションではこんな事が出来るよ

    • 値を計算するインスタンスのプロパティ/型プロパティの追加
    • インスタンスメソッド/型メソッドの追加
    • 新しいイニシャライザーの追加
    • 添字アクセスの追加
    • 型ネストの追加
    • 既存の型をプロトコルを適用した状態にする

メモ: 複数のエクステンションが重なった場合の動作は? 要確認

Extension Syntax

  • エクステンションの文法はこんな感じになるよ
extension SomeType {
    // new functionality to add to SomeType goes here
}
  • エクステンションでプロトコルを使う場合はこんな感じになるよ
extension SomeType: SomeProtocol, AnotherProtocol {
    // implementation of protocol requirements goes here
}

Initializers

  • エクステンションで便利イニシャライザーを追加することが出来るよ
    指定イニシャライザーを追加することは出来ないよ。
    deinitを追加することも出来ないよ。

Methods

  • エクステンションで既存の型にインスタンスメソッドを追加することが出来るよ
extension Int {
    func repetitions(task: () -> ()) {
        for i in 0..self {
            task()
        }
    }
}
  • これを使うとこんな感じになるよ
3.repetitions({
    println("Hello!")
    })
// Hello!
// Hello!
// Hello!
  • 後端記法を使うと、もっと簡潔に書けるよ
3.repetitions {
    println("Goodbye!")
}
// Goodbye!
// Goodbye!
// Goodbye!

メモ: ある意味、下手な動的言語より危険かも?

Mutating Instance Methods

  • エクステンションで構造体や列挙型を拡張する場合、selfやプロパティを変更するには『mutating』を指定する必要があるよ

とりあえずここまで。
(2014/06/13 作成)
(2014/09/25 修正)