はじめに
システム事業部の飯坂です。
今回は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?.メソッド().メソッド()
値に?
をつけます。
もしもfirstName
がnil
だったら、チェーン全体の結果がnil
になります。
2. if-letとguard-let
Optionalについて見たところで、次はif-let
とguard-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
メソッドの引数にクロージャを渡していて、
- 引数の末尾がクロージャの場合は
()
の外に書くことができる()
内に引数が他にない場合は()
も省略できる
というルールによってこの形になっています。
これは末尾クロージャ、後置クロージャなどと呼ばれます。
省略形がたくさん
調べてみると省略形はいくつか段階があることがわかります。
型推論される場合、クロージャ内の戻り値の型や引数も省略できます。
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プロパティといいます。
上記の例ではmetre
はkiloMetre
をもとに計算すればよいので、
自身は値を保持しない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の違いは何?
まずself
とSelf
は、その型の中からプロパティにアクセスする時に付けます。
// 型 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"
おわりに
基本ながら、細かく見ていくとわかっていないことが多かったです。
実際に使う時はより複雑な構文になりますが
基礎をしっかり身につけて活用したいと思います。
お読みいただきありがとうございました!