zenet_logo

-株式会社ゼネット技術ブログ-

【初心者】Swiftを初めて読む時に知りたかった文法の基本

はじめに

システム事業部の飯坂です。

今回はiOS開発に使われる言語、Swiftについて書きます。
見慣れない書き方にあたっては検索を繰り返す初心者でしたが
先日、Swiftの文法をあらためて学びました。

中でも自分が特に知りたかった点をピックアップし、記録します。


この記事を読むのにオススメな人
  • Swiftのコードを初めて見て、慣れない文法に困惑している人
  • クロージャ、Optional型などの基本文法をざっくり俯瞰したい人
目次

1. Optional型

var firstName: String?
var lastName: String

Swift初見時の私「?がついている変数は何」

?がついているのはOptional型、nilを許容する特殊な型です。

Swiftでは安全性の理由から、通常の変数や定数にnilを代入できません。
しかしnilを許容したい場合のために、nilを唯一格納できる型としてOptionalがあります。

概要だけ見た私「じゃあString?はnilが入るようにStringが変化したものか」

違いました。
String?はStringの変種ではなくOptional型です。
Stringまたはnilを格納できるOptionalオブジェクトであって、
OptionalにStringが包まれているイメージです。

// 簡単な書き方
var firstName: String?
// 正式な書き方
var firstName: Optional<String>


値を利用する際はStringの値を取り出す必要があります。
こちらは!で強制的に取り出す方法です。

var firstName: String? = "Maria"

print(firstName)
// =>Optional("Maria")
// Optional型から取り出していない。このままでは利用不可

print(firstName!)
// => "Maria"
// 変数名! で強制アンラップ

!は変数がnilだった場合エラーが発生する書き方なので注意が必要です。
Optionalからの安全な取り出し方については
後述の「if-letとguard-let」も参照ください。



Optionalの値に対してメソッドチェーンを行う場合、

firstName?.メソッド().メソッド()

値に?をつけます。
もしもfirstNamenilだったら、チェーン全体の結果がnilになります。

2. if-letとguard-let

Optionalについて見たところで、次はif-letguard-let文です。
Optionalな値を安全に処理するために使います。
コードを見た方が早いのでまずは並べてみます。

if-let
// optionalMoneyはnilかもしれないオブジェクト

if let money = optionalMoney {
    money.何らかの処理  // 値があった場合
} else {
    print("moneyありませんでした")
}
guard-let
// optionalMoneyはnilかもしれないオブジェクト

guard let money = optionalMoney else {
    return  // nilだった場合に処理を抜ける記述
}

money.何らかの処理

if-letはnilチェックを行なって
値があったらOptionalから取り出し使用することができます。
nilだった場合の処理もelseに書くことができます。

それに対してguard-letは早期リターンです。
nilだったらこの先は処理をしない、という場合に使います。


3. ディクショナリ

let drinkPrices: [String: Int] = ["お茶": 100, "コーヒー": 150, "紅茶": 500]

Swift初見時の私「rubyのハッシュっぽいものがある」

Swiftのディクショナリはキーと値をセットで持つデータ構造です。
他の言語にも似たものがあると思います。

値の取得はキーを指定します。

let teaPrice: Int? = drinkPrices["お茶"]

ここで見ておきたいのは定数teaPriceの型がOptionalのInt?になっている点です。
存在しないキーを指定した場合はnilが取得されます。
nilが入る可能性があるためOptional型として宣言します。

ちなみに上記のディクショナリの宣言はシンプルな書き方(糖衣構文)です。
以下の二行は同じ意味です。

let drinkPrices: [String: Int]
let drinkPrices: Dictionary<String, Int>



繰り返し処理でキーと値を取り出すことができます。

for (name, price) in drinkPrices {
    print("\(name)\(price)円です")
}

ディクショナリには関係ないですが、
上記のように"\(変数)"とすると文字列の中で変数を展開します。
よく忘れてしまいますがMacの場合\はoption + ¥ で入力できます。



4. クロージャ

let smallNumbers = numbers.filter { (number: Int) -> Bool in
    return number < 5
}

Swift初見時の私「後ろがいきなり{ }で囲まれている。これは何」

クロージャは名前のついていない関数です。
どういうことかというと、まず通常の関数は以下のような形をしています。

// 通常の関数の記述方法
func selectSmallNumber (number: Int) -> Bool {
    return number < 5   
}

// func 関数名 (引数: 型) -> 戻り値の型 { 処理内容 }

この関数にはselectSmallNumberという名前がついています。
同じ内容をクロージャでこのように書けます。

// クロージャでの記述方法
{ (number: Int) -> Bool in
    number < 5   
}

// { (引数: 型) -> 戻り値の型 in 処理内容 }

コードの量が少なくなりました。処理が一行ならreturnも省略OKです。

何度も呼び出して使う時はselectSmallNumberという名前が欲しいですが、
メソッドの引数など この場限りの処理では名前が要らないので
クロージャが使われます。

末尾クロージャ

一番初めに書いたコードをもう一度見てみます。

let smallNumbers = numbers.filter { (number: Int) -> Bool in
    return number < 5
}

これは配列のfilterメソッドの引数にクロージャを渡していて、

  1. 引数の末尾がクロージャの場合は()の外に書くことができる
  2. ()内に引数が他にない場合は()も省略できる

というルールによってこの形になっています。
これは末尾クロージャ、後置クロージャなどと呼ばれます。

省略形がたくさん

調べてみると省略形はいくつか段階があることがわかります。
型推論される場合、クロージャ内の戻り値の型や引数も省略できます。

let numbers = [1, 4, 7]

// いろいろ省略すると……
let smallNumbers = numbers.filter { $0 < 5 }

???

ここまで省略できるみたいです。
$0ではクロージャの引数が参照されます。(この場合、numbers配列の中身)
いきなりこれを見たらなんだかよくわかりませんね。


5. Computedプロパティと構造体

// キロメートル
var kiloMetre: Int = 1

// メートル
var metre: Int {
    return self.kiloMetre * 1000
}

Computedプロパティは
アクセスされる度に値を計算して返すプロパティです。

Computedプロパティに対して
値を保持するものはStoredプロパティといいます。

上記の例ではmetrekiloMetreをもとに計算すればよいので、
自身は値を保持しないComputedプロパティとして宣言されています。



上記のコードでは記述が省略されていますが、
return self.kiloMetre * 1000はgetterです。
Computedプロパティにはgetterと、必要な場合はsetterも定義できます。

struct Driving {
    // キロメートル
    var kiloMetre: Int

    // メートル
    var metre: Int {
        get {
            return self.kiloMetre * 1000
        }
        set {
            // 計算元のプロパティに値を設定
            // newValueと書くと渡された値を参照できる
            self.kiloMetre = newValue / 1000
        }
    }
}

var drivingToday = Driving()
drivingToday.metre = 3000
// kiloMetre = 3 が設定された

始めに書いた例のように、
getterしかない場合はget {}を書かなくてもOKです。


Computedプロパティはわかったけど……

上のコードを眺めている時、
別のことが気になってしまいました。

「大文字のSelfと小文字のselfがあるらしい……」

「よく見たらstructって何」

〜検索〜


selfとSelfの違いは何?

まずselfSelfは、その型の中からプロパティにアクセスする時に付けます。

// 型 Driving
struct Driving {
    static var vehicle: String = "bike"

    // メートル
    var metre: Int = 2000

    // キロメートル
    var kiloMetre: Int {
        get {
            // selfをつける
            return self.metre / 1000
        }
    }

    func printVihicle() {
        // Selfをつける
        print(Self.vehicle)
    }
}

大文字のSelfをつけて呼び出しているvar vehicleを見てみると
staticで宣言されています。

staticって何?

staticがついているプロパティは型自身に属します。
どのインスタンスからでも共通で使う値です。

一方var metreなどのプロパティは各インスタンスに属する値なので、
インスタンスを生成して参照します。

var drivingToday = Driving()
drivingToday.metre // インスタンスから参照
Driving.vehicle   // インスタンス化せず参照(static)


Driving.metre    // これはNG


まとめるとselfの大文字・小文字は

  • 型に属するものはSelfをつけて参照
  • インスタンスに属するものはselfをつけて参照

ということでした。

  • Selfは型自身を指している
  • selfはインスタンス自身を指している

このことを覚えておくと良さそうです。

また、staticはメソッドにも付けられます。


structって何?
struct Person {
    // ここにプロパティとかメソッド
}
class Person {
    // ここにプロパティとかメソッド
}

swift初見時の私「classだけ知ってます」

どうやら「構造体(structure)」というものがあるようです。


構造体は、クラスに似ています。
どちらもプロパティやメソッドを定義することができます。

構造体は継承ができない等、いくつか違いはありますが

  • クラスは参照型
  • 構造体は値型

というところが大きいです。
最後にこれを簡単にメモしておこうと思います。

構造体は値型
struct Person {
    var status: String
}

// インスタンス生成
var me = Person(status: "sleeping")
me.status   // "sleeping"

// 別の変数に代入
// meが持つ"sleeping"という値自体がyouに代入される
var you = me

// コピー先を変更しても……
you.status = "busy"

// コピー元の変数には影響しない
me.status   // "sleeping"


クラスは参照型
class Person {
    var status: String
}

// インスタンス生成
var me = Person(status: "sleeping")
me.status   // "sleeping"

// 別の変数に代入
// meが持つ"sleeping"という値ではなく
// 値が格納されている場所を代入している
// meとyouが同じ場所を参照する
var you = me

// なので、コピー先を変更すると……
you.status = "busy"

// コピー元も変わってしまうので注意
me.status   // "busy"


おわりに

基本ながら、細かく見ていくとわかっていないことが多かったです。
実際に使う時はより複雑な構文になりますが
基礎をしっかり身につけて活用したいと思います。

お読みいただきありがとうございました!

参考

qiita.com

www.swiftlangjp.com