Swift
メインページ > 工学 > 情報技術 > プログラミング > Swift
Swiftとは、Appleが作成したオープンソースのプログラミング言語である。なお、iPadアプリ 「Swift Playgrounds」では、子供向けにSwiftの基本を解説している。
目次編集
- Swift/
- Swift/文法 - 主に制御構造、関数定義について
- Swift/オブジェクト指向 - 主に列挙体、クラス、構造体について
- Swift/非同期処理 - 主にGrand Central Dispatchによる非同期処理について
環境準備編集
Windows編集
Windows10以降しか対応していない。SwiftのWindows10への対応がニュースになったが『Appleのプログラミング言語「Swift」がWindows 10へ移植、電卓デモがお披露目される』樽井 秀人 2020年9月23日 13:08、裏を返せば、Windows8以前のOSはサポート外である。
Macの場合編集
- ※未作成
Linuxの場合編集
Fedoraの場合、コマンド
sudo dnf install swift-lang
でインストール完了。以上。
Fedoraの場合、Swift公式サイトにあるRPMはダウンロードしなくて済む。
実行方法編集
Hello World編集
コードは
print( "Hello, World!" )
で十分。これをテキストエディタでコードを書き、たとえば hello.swift
などのファイル名で保存する。swiftで推奨される拡張子は「.swift」である。
コンパイルする場合編集
実行方法は、まずコンパイルのために
swiftc ファイル名.swift
をする。
すると、標準設定ならカレントフォルダ・カレントディレクトリなどシステム標準の場所に ファイル名 の実行ファイルが出来るので、あとはそれを通常の実行ファイルと同じ方法で実行すればいい。
不慣れな人に説明すると、具体的には
./ファイル名
のようなコマンドで実行できる。
インタプリタの場合編集
コマンド
swift ファイル名.swift
で、インタプリタとしてコンパイル無しで実行できる。
かつて「swift run」というコマンドだったが、現代では廃止され、上記のようなコマンドになっている。
対話モードの場合編集
コマンド
swift
で対話モードになる。
終了したい場合、Ctrl + D ボタンで終了する。
定数と変数編集
基本編集
定数はlet
キーワードで、変数はvar
キーワードによって宣言する。
let 定数名: 型 = 式 var 変数名: 型 = 式
なお、変数や定数を文字出力したい場合、
var a: Int = 47 print( a )
のように記述する。型 Int
は冒頭が大文字でなければならない。小文字 int だとエラーになる。
なお、これを実行すると
47
と表示される。
定数名や変数名には、「print」などの標準ライブラリ関数やSwiftのキーワードは使用できません。よく分からなければ、初心者のうちは「a」とか「b」とかの変数名を利用していれば、学習としては問題ないでしょう。
ほか、変数名などを数字で始めることは出来ません。
また、変数名などの中に半角スペースを入れるのは許されません。(半角スペースがあると2個の単語として認識され、エラーになります。)
下記のように、先に変数名だけ宣言しておいて、あとで値を代入することも可能である。
var a: Int a = 31 print( a )
- 実行結果
31
仕様編集
型推論が可能な場合には型アノテーション(type annotation; : 型名
の部分)を省略できる。
let a: Double = 3.141592 let a = 3.141592 // 型名を省略する
なお、いかなる数値型の変数についても暗黙の型変換(cast)が行われることはなく、型の違う数値同士の演算、右辺と左辺で型の異なる代入は全てコンパイルエラーとなる。一方、数値リテラルについては、整数型から浮動小数点型への暗黙の型変換が行われることがある。
let b: Int = 1 let c: UInt = b // コンパイルエラー let d: Int = b + 1.0 // コンパイルエラー
基本データ型編集
数値編集
符号付き整数の型には、Int
、Int8
、Int16
、Int32
、Int64
がある。Int
は、32ビット環境ではInt32
と同じサイズ、64ビット環境ではInt64
と同じ。
符号無し整数の型には、UInt
、UInt8
、UInt16
、UInt32
、UInt64
がある。UInt
は、32ビット環境ではUInt32
と同じサイズ、64ビット環境ではUInt64
と同じ。
浮動小数点数の型には、IEEE 754 単精度Float
と、倍精度のDouble
がある。
先述の通り、上記の型に対して暗黙の型変換(cast)が行われることは一切ない。
「int」など小文字だとエラーになる。冒頭は大文字でInt
のように書かなければならない。
文字列編集
Swiftの文字列はString
型、文字はCharacter
型である。
- コード例
var a: String = "good morning" print(a)
- 実行結果
good morning
二重引用符 ( " から次の " まで)の中に、変数に代入したい文字または文字列を書く。変数のセクションではいちいち説明しなかったかもしれないが、変数には数値だけでなく文字列や文字も代入できる。
文字や文字列を代入する場合、シングル引用符「'」だとエラーになる。二重引用符を使うこと。
文字列と別の文字列の連結は、+
演算子で行える。
- コード例
var greet1 = "good" var space = " " var greet2 = "afternoon" var msg = greet1 + space + greet2 print(msg)
- 実行結果
good afternoon
なお、「greet1 + " "」のように直接的に引用符で指定した文字列と変数を連結しようとしても、エラーになる。よって上記では、別途、変数「space」を用意してある。
表示したい文字列中に変数値を埋め込む場合、下記の方法のいずれかを使えばいい。String()
でキャストする方法か、または\()
を使う方法である。
- コード例
// 方法1 var a = 14 var msg1 = "彼は" + String(a) + "歳" print(msg1) // 方法2 var b = 16 var msg2 = "彼女は\(b)歳" print(msg2)
- 実行結果
彼は14歳 彼女は16歳
UTF-16表現ではサロゲートペアを要する拡張領域の文字も1文字として扱う。
下記のように、オブジェクトの文字数をカウントするcountプロパティが用意されているので、それでカウントするとコメント(// より行末までが実行時には無視される)にある結果が表示される。
import Foundation let str = "aαあ𪚲" print(str.utf8.count) // 10 print(str.utf16.count) // 5 print((str as NSString).length) // 5 = length in UTF-16 print(str._bridgeToObjectiveC().length) // 5 let char = str.character(at:3) print(type(of: char)) // UInt16 print(String(format:"%x", char)) // d869
NSString の利用には Foundation のインポートが必要なので、上記コードではインポートしている。
配列編集
配列は、Array<型>
ないし[型]
と宣言する。より直感的な後者が推奨されている。
var arrayOfChars: [Character] // 宣言のみ var arrayOfInts = [Int]() // 空の配列として初期化
let fibs = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] // [Int] let transcendentals = [ 2.7182818284590451, 3.1415926535897931 ] // [Double] let falsies = [false, 0, 0.0, ""] // [Any] 型の混在
なお、[]
は空の配列リテラルであるが、要素の型を何らかの形で指定する必要がある
var ints: [Int] = [] var doubles: [Double] = [] var any: [Any] = [] var unknown = [] // 型推論ができないため、コンパイルエラー
配列の結合、代入
var ints1: ArraySlice<Int> = [10,20,30] var ints2: ArraySlice<Int> = [40,50] ints1 = ints1 + ints2 // [10,20,30,40,50] ints2[1] = 55 // [40,55] ints1[1...2] = ints2 // [10,40,55,40,50] ints1 = ints2 // [40,55]
空の定義を宣言した場合は、下記のように、あとで例えば.append
などで具体的な値とともに項を追加する必要がある。なお、値なしで配列領域だけ確保することは推奨されておらず、標準の方法ではエラーになる。
var str = [Int](); str.append(5) print(str[0])
- 実行結果
5
基本操作
var arr = [0, 1, 2, 3, 4, 5, 6] arr.append(7) print(arr) // [0, 1, 2, 3, 4, 5, 6, 7] arr += [8, 9, 10] print(arr) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] arr.insert(11, at:2) print(arr) // [0, 1, 11, 2, 3, 4, 5, 6, 7, 8, 9, 10] arr[2] = 12 print(arr) // [0, 1, 12, 2, 3, 4, 5, 6, 7, 8, 9, 10] arr[2...2] = [11, 12, 13] print(arr) // [0, 1, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9, 10] arr.remove(at:0) print(arr) // [1, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9, 10] arr.removeLast() print(arr) // [1, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9] arr[5...] = [99] print(arr) // [1, 11, 12, 13, 2, 99] let index13 = arr.index(of: 13) print(index13) // Optional(3)
let squared = arr.map{ i in i * i } print(squared) // [1, 121, 144, 169, 4, 9801] let filtered = arr.filter{ n in n > 3 } print(filtered) // [11, 12, 13, 99] let sorted = arr.sorted{ (a, b) in a > b } print(sorted) // [99, 13, 12, 11, 2, 1] let sum = arr.reduce(0){ (s, n) in s + n } print(sum) // 138 arr.forEach{ n in print(n) } // 1↵121↵144↵169↵4↵9801
let arr1 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] let arr1_1 = arr1.map{ a -> [Int] in a.reversed() } print(arr1_1) // [[3, 2, 1], [6, 5, 4], [9, 8, 7]] let arr1_2 = arr1.flatMap{ a -> [Int] in a.reversed() } print(arr1_2) // [3, 2, 1, 6, 5, 4, 9, 8, 7] let arr2 = [1, 2, nil, 4] let arr2_1 = arr2.map{ n -> Int? in n } print(arr2_1) // [Optional(1), Optional(2), nil, Optional(4)] let arr2_2 = arr2.flatMap{ n -> Int? in n } print(arr2_2) // [1, 2, 4]
セット編集
重複のないコレクション型
var set = [3, 1, 4, 1, 5, 9, 2, 6] as Set print(set)
基本操作
set.insert(5) print(set) // [1, 3, 4, 9, 6, 5, 2] set.remove(1) print(set) // [3, 4, 9, 6, 5, 2] print(set.contains(3)) // true var arr = set.sorted() print(arr) // [2, 3, 4, 5, 6, 9] var digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] as Set print(set.isSubset(of: digits)) // true
辞書編集
Swiftでは、連想配列は辞書(Dictionary)と呼ばれる。
辞書は総称型であり、キーの型KeyType
、値の型ValueType
を持つ辞書はDictionary<KeyType,ValueType>
または[KeyType: ValueType]
と宣言する。Array同様、より直感的な後者が推奨されている。
var name2num = [String:Int]() name2num["zero"] = 0 name2num["one"] = 1
let name2num = ["zero":0, "one":1, "two":2] // Dictionary<String, Int>
[:]
が空の辞書リテラルであるが、キーの型および値の型が指定されている必要がある点は配列と同様である。
var dict: [String: Int] = [:] var unknowndict = [:] // 型推論ができないため、コンパイルエラー
基本操作
var name2num = ["zero":0, "one":1, "two":2] name2num["three"] = 3 print(name2num) // ["two": 2, "one": 1, "three": 3, "zero": 0] name2num["zero"] = nil print(name2num) // ["two": 2, "one": 1, "three": 3] let one = name2num["one"] print(one) // Optional(1) print(name2num.keys) // ["two", "one", "three"] print(name2num.values) // [2, 1, 3] for element in name2num { print(element) // (key: "two", value: 2) ↵(key: "one", value: 1) ↵(key: "three", value: 3) } for (key, value) in name2num { print("\(key) = \(value)") // two = 2 ↵one = 1 ↵three = 3 } name2num.forEach{ (key, value) in print("\(key) = \(value)") // two = 2 ↵one = 1 ↵three = 3 }
タプル編集
let errorStatus: (Int, String) = (404, "Not Found")
let errorStatus = (404, "Not Found")
タプルの各要素には名前をつけることができる。名前付きタプルに対して、数値インデックスによって各要素にアクセスすることも可能。
let okStatus = (code: 200, message: "OK") print("\(okStatus.code): \(okStatus.message)") print("\(okStatus.0): \(okStatus.1)") // 200: OK // 200: OK
代入式で定数や変数に分解することができる。この際にアンダースコアを指定すると、その値を無視することができる。
let (_, message) = okStatus
Optional型編集
Swiftでは安全性のため、ある型の定数や変数にnil
を代入することは禁止されている。
変数にnil
を代入可能にするには、Optional
型でラップする。任意の型のOptional
型は、ラップしたい型名の後ろに"?
"を付けて表す。
var n: Int = nil // コンパイルエラー var m: Int? = nil // Optional型はnilを代入可能 var l: Optional<Int> = nil // Int? は Optional<Int> の略記である
var o:Int! = nil // 暗黙的開示Optional型。自動的に強制アンラップされるが、nilの場合に実行時エラーとなる var p:ImplicitlyUnwrappedOptional<Int> = nil // Int! は ImplicitlyUnwrappedOptional<Int> の略記である
// 文字列の配列の中から指定した文字列を探す関数 func findString(data: [String], key: String) -> Int? { for index in 0..<data.count { if data[index] == key { return index } } return nil }
Optional<Type>
型の値からType
型の値を取り出すことを、アンラップするという。
Optional
型の値を強制的にアンラップするには、値のうしろに"!
"を付ける。アンラップしようとした値がnilの場合は実行時エラーが発生する。
let fruits = ["apple", "banana", "orange"] let possibleKiwiIndex = findString(data:fruits, key:"kiwi") // Int?型 if possibleKiwiIndex != nil { let kiwiIndex = possibleKiwiIndex! print("キウイは\(kiwiIndex)番目に見つかりました") } else { print("キウイは見つかりませんでした") }
if文とwhile文ではOptional束縛(optional binding)を記述できる。
if let kiwiIndex = find(fruits, "kiwi") { print("キウイは\(kiwiIndex)番目に見つかりました") } else { print("キウイは見つかりませんでした") }
Optional
型の変数のメソッドやプロパティを呼び出す際には、Optional Chainingを記述できる。
var possibleOrangeIndex = findString(data:fruits, key:"orange") if possibleOrangeIndex?.distance(to:1).signum() == -1 { print("オレンジは2番目より後にあります") }
var path:String? = "/path/to/an/invisible/.file" var isInvisible = path?.split(separator:"/").last?.hasPrefix(".") print(isInvisible) // Optional(true)
演算子編集
算術演算子編集
文法 | 意味 |
---|---|
+ |
加算 |
- |
減算 |
* |
乗算 |
/ |
除算(整数型のゼロ除算はエラーとなる) |
% |
剰余(整数型のゼロでの剰余演算はエラーとなる) |
&+ |
加算(オーバーフローを無視する) |
&- |
減算(オーバーフローを無視する) |
&* |
乗算(オーバーフローを無視する) |
&/ |
除算(オーバーフローを無視し、ゼロ除算の結果は0 となる)(※廃止済み)
|
&% |
剰余(オーバーフローを無視し、ゼロでの剰余演算の結果は0 となる)(※廃止済み)
|
文法 | 意味 |
---|---|
+ |
単項プラス |
- |
単項マイナス(符号反転) |
++ |
インクリメント(式はインクリメントされた後の値を返す)※Swift 3.0で廃止[1] |
-- |
デクリメント(式はデクリメントされた後の値を返す)※Swift 3.0で廃止[1] |
文法 | 意味 |
---|---|
++ |
インクリメント(式はインクリメントされる前の値を返す)※Swift 3.0で廃止[1] |
-- |
デクリメント(式はデクリメントされる前の値を返す)※Swift 3.0で廃止[1] |
比較演算子編集
|
|
|
print(1...5 ~= 3) // true
var str = "mission control" print(str.range(of: "control")! ~= str.index(of: "c")!) // true
論理演算子編集
文法 | 意味 |
---|---|
&& |
論理AND |
|| |
論理OR |
! |
論理NOT |
三項演算子編集
文法 | 意味 |
---|---|
条件 ? 式1 : 式2 |
条件が真のとき式1の値を、偽のとき式2の値を返す |
nil結合演算子(Nil Coalescing Operator)編集
文法 | 意味 |
---|---|
?? |
左オペランドにはT? 型、右オペランドにはT 型の値をとり、左オペランドに値が存在していればアンラップしてその値を返し、左オペランドが nil であれば右オペランドの値を返す
|
"x".toInt() ?? 0 // 0 "5".toInt() ?? 0 // 5
ビット演算子編集
文法 | 意味 |
---|---|
<< |
左シフト |
>> |
右シフト(左オペランドが符号付き整数の場合は算術シフト、符号無し整数の場合は論理シフト) |
& |
AND |
| |
OR |
^ |
XOR |
~ |
NOT(ビット反転) |
代入演算子編集
|
|
範囲演算子(Range operators)編集
文法 | 意味 |
---|---|
..< |
半分開いた範囲(終端を含まない):半開区間 |
... |
閉じた範囲(終端を含む):閉区間 |
キャスト演算子編集
文法 | 意味 |
---|---|
is |
型検査 |
as |
型キャスト |
as? |
Optional型へのキャスト キャストできない場合はnilとなる |
as! |
強制型キャスト キャストできない場合は実行時エラーとなる |
- キャスト演算子
var arr:[Any] = [1, 2.0, "3", -4] for item in arr { let intItem = item as? Int print(intItem) }
- 実行結果
Optional(1) nil nil Optional(-4)
import Foundation var array1 = [1, 2, 3, 4] as NSArray var mutableArray1 = array1 as? NSMutableArray // ダウンキャストできないので、nilになる // ※補足: 上記の例の場合、Mutableな状態で取得したければ array1.mutableCopy() を行うべき。 var mutableArray2 = [1, 2, 3, 4] as NSMutableArray var array2 = mutableArray2 as NSArray var mutableArray2_2 = array2 as? NSMutableArray // 元々mutableArray2の型はNSMutableArrayなので、キャストに成功する
その他編集
識別子にはたいていのUnicode文字を用いることができる。
let リンゴの数 = 3 let みかんの数 = 5
文字列リテラルの中にある\(...)
には、式の結果が展開される
let リンゴ説明 = "私は\(リンゴの数)個のリンゴを持っている。" // ”私は3個のリンゴを持っている。" let 果物説明 = "私は\(リンゴの数 + みかんの数)個の果物を持っている。" //"私は8個の果物を持っている。"
ヒアドキュメントには、ダブルクォーテーション3つを使用する。ヒアドキュメント内の行頭の空白は自動的にトリミングされる。
let tanka = """ 田子の浦に うち出でてみれば 白妙の 富士の高嶺に 雪は降りつつ """ print(tanka)
数値リテラルのプレフィックスは"0b
"で2進数、"0o
"で8進数、"0x
"で16進数を表す。
let dec = 29 let bin = 0b11101 // 2進数で29 let oct = 0o35 // 8進数で29 let hex = 0x1D // 16進数で29
浮動小数点リテラルは、通常の十進数表記に加え16進数表記もサポートしている。
let π = 3.1415926535897931 let pi = 0x1.921fb54442d18p+1 // NSString(format:"%a", π) の出力と同様
整数型と浮動小数点型のどちらでも、コードの見やすさのためにアンダースコア _ を桁の区切りとして挿入できる。
let threeHundledMillion = 300_000_000 let bitMask: UInt8 = 0b0010_0000
アンダースコアは、代入文で代入する値を無視したいときに、仮の代入先として使用できる。
var s:String? = "String" if let _ = s { print("sはnilではありません。") }
for _ in 0..<5 { print("repeat") }
let result = (404, "Not Found", false) let (_, message, _) = result
他言語との比較編集
C言語との類似点編集
- ほとんどのC言語の演算子はSwiftでも使用できる。
- ただし、Swiftではオーバーフローを伴う数値演算のサポートのための演算子が追加されている。
- 中括弧は、文をグループ化するために使用される。
- 等号1つ
=
は代入、2つ==
は等価比較を意味する。- この他に、Swiftでは等号3つ
===
は同じオブジェクトを参照しているかどうかを確認するための演算子を意味する。
- この他に、Swiftでは等号3つ
while
、if
、for
等の制御文が類似している。- ただし、Swiftでは拡張機能を有する。例えば、
while
、if
文はパターンマッチングや条件付きOptionalアンラップをサポートする。
- ただし、Swiftでは拡張機能を有する。例えば、
- 角括弧は、配列の宣言と配列の要素取得の両方で使用される。
Objective-Cとの類似点編集
- 基本的な数値型
Int
、UInt
、Float
、Double
等のサポート。 - クラスメソッドは、インスタンスメソッドと同様に継承される。クラスメソッド内の
self
は、メソッドが呼び出されたクラスを意味する。 for
...in
列挙構文のサポート。
Objective-Cとの相違点編集
- 文はセミコロン(
;
)で終わる必要はない。しかし、1行に複数の文を記述する際に使用することができる。 - ヘッダーファイルが存在しない。
/*
~*/
によるコメントはネストできる。- 型推論のサポート。
- ジェネリックプログラミングのサポート。
- 関数は第一級オブジェクトである。
- 演算子はクラスに対して再定義(演算子のオーバーロード)でき、新しい演算子を定義できる。
- 文字列はUnicodeを完全にサポートする。ほとんどのUnicode文字は識別子や演算子でも使用できる。
- 例外処理は存在しない。Swift 2では例外処理とは互換性のない別のエラー処理モデルが導入されている。
- バグの原因となるC言語ファミリーの特徴がいくつか削除されている。
- デフォルトでは、ポインタは公開されていない。プログラマが参照の管理をする必要はない。
- 変数割り当ては値を返さない。これにより、
=
と==
の誤用を防ぐことができる。 switch
文内でbreak
を行う必要はない。明示的にfallthrough
を行わない限り次のcase
にフォールスルーすることはない。- 変数と定数は常に初期化され、配列の境界は常にチェックされる。
- 算術オーバーフローは実行時エラーとしてトラップされる。オーバーフローを許可する演算子は
&+
、&-
、&*
、&/
、&%
として定義される。また、全ての整数型にはプロパティmin
、max
が定義されており、潜在的なオーバーフローのチェックに利用することができる。 - ブロックを用いない1行の
if
文、while
文はサポートされていない。 - Off-by-oneエラーの原因となるC言語スタイルの
for (int i = 0; i < c; i++)
文は、Swift 3で削除された。 - インクリメント演算子
++
、デクリメント演算子--
は、Swift 3で削除された。