LoginSignup
30
30

More than 5 years have passed since last update.

The Swift Programming Language ゆるふわ和訳 その2 ~A Swift Tour~

Last updated at Posted at 2014-06-16

原文: Apple Inc. “The Swift Programming Language”。 iBooks. https://itun.es/jp/jEUH0.l

Swiftツアー

その1からの続きです

オブジェクトとクラス

クラス

  • class の後にクラス名でクラスを定義します
  • クラス内のプロパティの宣言は、定数や変数の宣言と同じ書き方です
  • メソッドも、関数の定義と同じ書き方です
class Shape {
    var numberOfSides = 0
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

オブジェクト

  • インスタンスは、クラス名に()をつけて作成します
  • インスタンスに.でプロパティやメソッドにアクセスします
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

コンストラクタ

  • 上のShapeクラスはコンストラクタをもっていません コンスラクタを作るには、initを使います。
class NamedShape {
    var numberOfSides: Int = 0
    var name: String

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

    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}
  • コンストラクタ内にて、selfをつけることでプロパティのnameと引数のnameが区別されています
  • すべてのプロパティには値を設定しなければいけません。numberOfSidesのように宣言時に設定するか、nameのようにコンストラクタで設定させます

デストラクタ

  • デストラクタにはdeinitをつかいます。

継承

  • クラスの継承は、クラス名の後ろにコロンと継承するクラス名をつけます
  • 基本的にクラスを記述するときは、標準基底クラスを継承する必要はありません。
  • スーパークラスの関数をオーバーライド するときは、overrideをつけます。
  • overrideをつけずに関数をオーバーライドしようとするとエラーになります。また、スーパークラスがもっていない関数名に対してoverrideをつけた関数を定義してもエラーになります
class Square: NamedShape {
    var sideLength: Double

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 4
    }

    func area() ->  Double {
        return sideLength * sideLength
    }

    override func simpleDescription() -> String {
        return "A square with sides of length \(sideLength)."
    }
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()

ゲッター・セッター

  • プロパティにはゲッターとセッターを設定することができます
class EquilateralTriangle: NamedShape {
    var sideLength: Double = 0.0

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }

    var perimeter: Double {
    get {
        return 3.0 * sideLength
    }
    set {
        sideLength = newValue / 3.0
    }
    }

    override func simpleDescription() -> String {
        return "An equilateral triagle with sides of length \(sideLength)."
    }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
triangle.perimeter
triangle.perimeter = 9.9
triangle.sideLength
  • 上記のperimeterのセッターでは、受け取る値に暗黙的にnewValueという名前がつきます。明示的に名前をつけるときはsetの後ろに括弧をつけて名前を入れます

  • 上記のEquilateralTriangleクラスのコンストラクタでは3種類のステップが行われています
    1.サブクラスで定義されたプロパティに値を設定している
    2.スーパークラスのコンストラクタを呼ぶ
    3.スーパークラスで定義されているプロパティの値を変更する。関数やゲッター、セッターを使うその他の初期設定もこの時点で行うことができます

willset・didset

  • プロパティに値が設定された後または前に処理を行いたいときは、willsetとdidsetを使います
  • 下記ではtriangleのsideLengthはsquareのsideLengthと常に同じになるようになっています
class TriangleAndSquare {
    var triangle: EquilateralTriangle {
    willSet {
        square.sideLength = newValue.sideLength
    }
    }
    var square: Square {
    willSet {
        triangle.sideLength = newValue.sideLength
    }
    }
    init(size: Double, name: String) {
        square = Square(sideLength: size, name: name)
        triangle = EquilateralTriangle(sideLength: size, name: name)
    }
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLength
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
triangleAndSquare.triangle.sideLength
  • 関数とクラスのメソッドの違いとして、関数の引数名は関数内でしか使われませんが、メソッドの引数名(1つ目の引数以外)は関数を呼ぶときにも使用します。
  • 基本的にメソッドの引数名はメソッド内で使う時も同じ名前で使用されますが、引数名に2つ目の名前をつけることができメソッド内で使用することができます。
class Counter {
    var count: Int = 0
    func incrementBy(amount: Int, numberOfTimes times: Int) {
        count += amount * times
    }
}
var counter = Counter()
counter.incrementBy(2, numberOfTimes: 7)

optional値

  • optional値(nilを含む値) への関数やプロパティへアクセスするときは?をつけます
  • ?がついている値がnilの場合、その後ろの処理は無視され、nilが返ります。nilかそうでない場合においても、最後の結果もoptional値になります
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength

列挙体と構造体

列挙体

  • 列挙体を作るにはenumを使います
  • クラスなどの様に列挙体もメソッドを持つことができます
enum Rank: Int {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
    case Jack, Queen, King
    func simpleDescription() -> String {
        switch self {
        case .Ace:
            return "ace"
        case .Jack:
            return "jack"
        case .Queen:
            return "queen"
        case .King:
            return "king"
        default:
            return String(self.toRaw())
        }
    }
}
let ace = Rank.Ace
let aceRawValue = ace.toRaw()
  • 上記の例では、列挙体の定数値の型はIntなので、最初の定数値しか指定できません。その他の定数値は順番に自動的に割り当てられます。
  • 定数値の型は他に文字列型や浮動小数型を指定することもできます
  • toRawとfromRow関数は定数と定数値の変換を行います
if let convertedRank = Rank.fromRaw(3) {
    let threeDescription = convertedRank.simpleDescription()
}
  • 列挙体の定数は、定数値にタグづけする為だけのものではいので、必ずしも定数値を設定してあげなくてもいい。
enum Suit {
    case Spades, Hearts, Diamonds, Clubs
    func simpleDescription() -> String {
        switch self {
        case .Spades:
            return "spades"
        case .Hearts:
            return "hearts"
        case .Diamonds:
            return "diamonds"
        case .Clubs:
            return "clubs"
        }
    }
}
let hearts = Suit.Hearts
let heartsDescription = hearts.simpleDescription()
  • 上記の"let hearts"の箇所では、heartsの型が定まっていないため、Suit.Heartsのように列挙体の定数をフルネームで指定しています。 一方、switchの中では.Heartsと省力された形で呼ばれています。なぜならselfはsuitであることが自明であるためです。値の型がわかってれば省略した呼び出し方がいつでもできます。

構造体

  • structを使い構造体を作成します
  • 構造体はクラスと同じようにメソッドやコンストラクタを持ちます
  • 構造体とクラスの一番の違いは、構造体を呼び出すときは毎回コピーされた構造が渡されますが、クラスの場合は参照が渡されます
struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()

Associated値

  • 列挙体はインスタンスに合わせた関連値(Associated値)を持つことが出来ます。同じ列挙体でもインスタンスごとに異なる関連値をもつことが出来ます。
  • 関連値はインスタンスを作成するときに設定します
  • 関連値と定数値は異なります。定数値は列挙体のすべてのインスタンスで同じ値になります。定数値の設定は列挙体を定義するときに行います
  • 下記の例は、日の出と日の入りの時刻をサーバから問い合わせて、時刻を返すかエラーを返します
enum ServerResponse {
    case Result(String, String)
    case Error(String)
}

let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")

switch success {
case let .Result(sunrise, sunset):
    let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
    let serverResponse = "Failure...  \(error)"
}
  • 日の出と日の入りの時刻はswitchのcaseで一致したServerResponseから渡されています

その3へ続く

30
30
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
30
30