Swift (プログラミング言語)
Swift | |
---|---|
登場時期 | 2014年6月2日 |
型付け | 強い静的型付け、型推論 |
Swift(スウィフト)は、アップルのiOSおよびmacOS、Linuxで利用出来るプログラミング言語。Worldwide Developers Conference (WWDC) 2014で発表された。アップル製OS上で動作するアプリケーションの開発に従来から用いられていたObjective-CやObjective-C++、C言語と共存することが意図されている[1]。
Swiftは、マルチパラダイムのコンパイラプログラミング言語であるが、XcodeのPlaygroundsの上やターミナルでインタラクティブにデバッグする事が可能である。
LLVMコンパイラが使われており、ライブコーディングに対応していることが特徴[2]。
並列スクリプト言語のSwift (並列スクリプト言語)(英語: Swift (parallel scripting language))とは名称を同じくするが、別言語。
Contents
- 1 歴史
- 2 特徴
- 3 サンプルコード
- 4 文法
- 5 メモリ管理(Automatic Reference Counting)
- 6 相互運用性
- 7 出典
- 8 外部リンク
歴史
Swiftは2010年にLLVMとClangの始祖であるChris Lattnerによって開発が始められた。その後アップル社内での4年間の開発期間を経て、2014年のWWDCにおいて一般に発表され、同時にアップルに開発者登録している開発者に対してベータ版の提供が開始された。
WWDC 2016の基調講演で、アップルはSwiftでのコード作成方法を教えることを目的とした、Swift Playgroundsという名称のiPad専用アプリ開発を発表した。2016年9月にリリースされた[3]このアプリは3Dビデオゲームのようなインターフェイスで表示され、コードの行が特定の順序で配置され実行されたときにフィードバックを提供する。2017年3月21日、Swift 3.1に対応し、日本語を含め5カ国語に対応したことを発表[4]。Swift 3.1は、2017年3月27日にリリースされた[5]。
2017年現在、Project Leadは、Chris Lattner同様にLLVMとClangの開発者である、AppleのTed Kremenekである[6]。
2017年9月19日、Xcode 9.0とともにSwift 4がリリースされた[7]。
2018年3月29日、Xcode 9.3とともにSwift 4.1がリリースされた[8]。
2018年6月4日、Xcode 10 betaとともにSwift 4.2がリリースされた [9]。
特徴
アップルはSwiftの発表に際して「モダン、安全、高速、インタラクティブ」を大きな特徴として挙げた。
- モダン
- クロージャやタプル、ジェネリックプログラミング、Optional型の採用などが挙げられる。
- 安全
- 静的な型チェック、変数の初期化の強制、数値型のオーバーフローの検査、自動参照カウントによるメモリ管理などが挙げられる。
また、if文のブレースの省略禁止、switch-case文は明示的に指定されない限りフォールスルーしないなど、既存のプログラミング言語において記述ミスによるバグが発生しやすかった部分を文法的に解決している。 - インタラクティブ
- Swiftはコンパイラ言語でありながら、インタプリタとしてスクリプトを実行することも可能で、対話実行環境(REPL)も用意されている。
Swiftと同時に発表されたXcodeバージョン6では、コードの実行結果をグラフィカルに確認しながら開発できるPlaygroundsが実装された。
サンプルコード
Hello World
print( "Hello, World!" ) // これだけで動いて、Hello, World! と出力される。
他
/*
* コメントはCスタイルの複数行コメントと…
*/
// C++スタイルの一行コメントの双方をサポートしている
// var name:Type = value でType型の変数nameを宣言し、valueで初期化する
var explicitDouble:Double = 70 // 70.0
/// 型が省略された場合は、型推論により初期値の型が適用される
var implicitInteger = 70 // Int
var implicitDouble = 70.0 // Double
// let name:Type = value でType型の定数nameにvalueを設定する。
// 型推論可能な場合、型の表記は省略できる。
let theAnswer = 42
// 識別子にはたいていのUnicode文字を用いることができる。
let リンゴの数 = 3
let みかんの数 = 5
// 文字列リテラル"..."の中にある\(expr)には、式exprの内容が展開される
let リンゴ説明 = "私は\(リンゴの数)個のリンゴを持っている。" // ”私は3個のリンゴを持っている。"
let 果物説明 = "私は\(リンゴの数 + みかんの数)個の果物を持っている。" //"私は8個の果物を持っている。"
// Swiftでは辞書も組み込みでサポートされている。
// 以下は Dictionary<String, Int> 型の定数辞書の定義の一例である。
let people = ["Anna": 67, "Bety": 8, "Jack": 33, "Sam": 25]
// 辞書の内容の列挙は for (key, value) in dict { ... }
for (name, age) in people {
print("\(name) is \(age) years old.")
}
// メソッドや関数は "func"文法を使って宣言する。
// パラメータ名の付け方に注意。-> で戻り値の型を宣言する
func sayHello(personName: String) -> String {
let greeting = "こんにちは、" + personName + "さん"
return greeting
}
// "こんにちは、サーバーさん"を出力
print(sayHello("サーバー"))
文法
定数と変数
定数はlet
キーワードで、変数はvar
キーワードによって宣言する。変数は値の再代入が可能だが、定数は後から値を変更することはできない。
let 定数名: 型 = 式
var 変数名: 型 = 式
Swiftでは型推論が可能な場合には型の記述を省略できる。
let pi: Double = 3.141592
let pi = 3.141592 // 型名を省略する
基本データ型
真偽値
Swiftの真偽値はBool
型であり、true
とfalse
どちらかの値をとる。
数値
符号付き整数の型には、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)が行われることはなく、型の違う数値同士の演算、右辺と左辺で型の異なる代入は全てコンパイルエラーとなる。Int32
型からInt64
型への変換や、64ビット環境におけるInt64
型からInt
型への変換など、非破壊的な変換においても暗黙の型変換が行われることはない。一方、数値リテラルについては、整数型から浮動小数点型への暗黙の型変換が行われることがある。
let intValue: Int = 1
let uintValue: UInt = intValue // コンパイルエラー (IntからUIntへの暗黙の型変換は行われない)
let intValue2: Int = intValue + 1.0 // コンパイルエラー (浮動小数点型の数値リテラルは整数型に変換されない)
let floatValue: Float = 1.0
let doubleValue: Double = floatValue // コンパイルエラー (FloatからDoubleへの暗黙の型変換は行われない)
let floatValue2: Float = floatValue + 1 // エラー無し (整数型の数値リテラルは浮動小数点型に変換される)
文字列
Swiftの文字列はString
型、文字はCharacter
型であり、Unicode文字一つを格納する。Objective-CやJavaやJavaScriptと異なり、UTF-16表現ではサロゲートペアを要する拡張領域の文字も一文字として扱う。
String
型の変数は内部的にはCocoa (Foundation) のNSString
クラスのインスタンスと互換性があり、キャストもしくは_bridgeToObjectiveC()
関数を用いることによりNSString
クラスのインスタンスメソッドを呼び出すことができる。
let str = "aαあ𪚲"
print(countElements(str)) // 4
print(str.utf16Count) // 5
print((str as NSString).length) // 5 = length in UTF-16
print(str._bridgeToObjectiveC().length) // 5
配列
Swiftではある型の要素の配列を、Array<型>
ないし[型]
と宣言する。どちらも同じ意味であるが、より直感的な後者が推奨されている。 配列内に型が混在する複合型配列は[Any]型となる
var arrayOfChars: [Character] // 宣言のみ
var arrayOfInts = [Int]() // 空の配列として初期化
配列リテラルは[ elem0, elem1 ...]
と表記され、型は各要素の型より推論される
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 = [] // compile error
配列の結合、代入
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]
辞書(Dictionary)
Swiftにおける連想配列は辞書(Dictionary)と呼ばれる。C++などと同様、Swiftの辞書は総称型であり、キーの型KeyType
、値の型ValueType
を持つ辞書はDictionary<KeyType,ValueType>
または[KeyType: ValueType]
と宣言する。Array同様、より直感的な後者が推奨されている。
var name2num = [String:Int]()
name2num["zero"] = 0
name2num["one"] = 1
リテラルは[k0:v0, k1:v1 ...]
と表記する。配列の場合と同様、型推論が効く
let name2num = ["zero":0, "one":1, "two":2] // Dictionary<String, Int>
[:]
が空の辞書リテラルであるが、キーの型および値の型が指定されている必要がある点は配列と同様である。
var dict: [String: Int] = [:] // OK
var unknowndict = [:] // compile error
複合型
通常のクラス、構造体、列挙型、プロトコルの型は名前付き型(named type)と呼ばれ、型の定義と同時にその型名が与えられる。標準ライブラリのIntやStringなども名前付き型である。
それに対して複合型(compound type)とは、それ自体に名前はついておらず、他の型の組み合わせから成り立つ型のことである。複合型にはタプル型と関数型の二種類がある。
タプル
タプルは複数の値を一時的にひとつにまとめて扱うための簡易的なデータ構造である。タプルは関数の多値返却などに利用される。
タプル型は括弧"()
"を使って(型1, 型2, ...)
の形式で定義し、どのような型同士でも組み合わせることが可能である。
HTTPステータスコードを表現するために、Int
型とString
型をひとまとめにした(Int, String)
型のタプルを使用する例を以下に示す。
let errorStatus: (Int, String) = (404, "Not Found")
これは型推論によって以下のようにも書ける。
let errorStatus = (404, "Not Found")
代入式で定数または変数に分解することができる。このとき定数/変数名にアンダースコアを指定すると、その値を無視することができる。
let (_, message) = errorStatus // ひとつ目の値は無視する
print(message)
// Not Found
タプルの各要素にはあらかじめ名前をつけることができ、その名前によって値に直接アクセスすることができる。また、インデックスを指定することでも値にアクセスできる。
let okStatus = (code: 200, message: "OK")
print("\(okStatus.code): \(okStatus.message)")
print("\(okStatus.0): \(okStatus.1)")
// 200: OK
// 200: OK
タプル以外の変数も1個のみの値をもつタプルとして設計されている。例えば以下はa
の値を正しく表示する。
let a = 10
print( a.0 )
関数型
関数型は引数の型 -> 返り値の型
で表される。例えば、Int
型とDouble
型の引数を1つずつ受け取りString
型の値を返す関数は、(Int, Double) -> String
型と表される。詳細は#関数の項を参照。
型エイリアス
型エイリアス(type alias)によって、既存の型に別名をつけることができる。型エイリアスの宣言はtypealias
キーワードによって行う。
typealias 新しい型名 = 既存の型名
制御構造
Perl等と同様に、帰結節や繰り返す文は(ブレースで囲まれた)複文(ブロック)でなければならない。また、Goなどと同様に、条件式を囲む括弧は不要である。
条件式に書けるのはBool
型かBool
型の式か、BooleanType
プロトコルを実装する型でなければならない。後者はboolValue
プロパティのBool
値が評価される。
if文
if 条件式1 {
文1
} else if 条件式2 {
文2
} else {
文3
}
switch文
C言語と違い、数値だけでなくあらゆるオブジェクトによって分岐することができる。また暗黙的にブレークされ、fallthrough
キーワードがある場合に限り、次のcase文にフォールスルーする。
switch 式 {
case パターン1:
println("case 1")
//暗黙的にbreakされる
case パターン2, パターン3: //複数の条件をコンマで区切って指定可能
print("case 2 or 3")
case パターン4 where 条件式: // where 条件式でパターンに制限を加える事が出来る
print("case 4")
fallthrough //fallthroughがある場合のみ次のcase文にフォールスルー
case パターン5: // let x の様にswitch式の値をcase本文内で使う変数に割り当てる事が出来る。 バリューバインディング(value binding)
print("case 5")
default:
print("default")
}
for/for-in文
for-in文によってコレクションや範囲の要素をイテレートできる。
let names = ["太郎", "花子", "一郎"]
for name in names {
print("\(name)さん、こんにちは!")
}
C言語風のfor文はSwift 3.0で廃止された[10]
for var index = 0; index < 5; index++ {
print("index: \(index)")
}
上記のコードは範囲(Range)を使ったfor-in文で次のように書ける
for index in 0..<5 {
print("index: \(index)")
}
while/repeat-while文
Swiftでは2.0でdo-while文がrepeat-whileに変更された。
while 条件式 { 文 } repeat { 文 } while 条件式
Optional型
Swiftでは安全性のため、ある型の定数や変数にnil
を代入することは禁止されている。
ある値が存在しないことを示すため変数にnil
を代入可能にしたい場合には、Optional
型でラップする。
任意の型のOptional
型は、ラップしたい型名の後ろに"?
"を付けて表す。
var n: Int = nil // コンパイルエラー
var m: Int? = nil // Optional型でラップすればnilを代入可能
var l: Optional<Int> = nil // Int? は Optional<Int> の略記である
// 文字列の配列の中から指定した文字列を探し、
// 見つかればそのインデックスを、見つからなければnilを返す関数。
// nilを返し得るので戻り値の型はInt型ではなくInt?型である。
func findString(data: [String], key: String) -> Int? {
for index in 0..<data.count {
if data[index] == key {
return index
}
}
return nil
}
let fruits = ["apple", "banana", "orange"]
let bananaIndex = findString(fruits, "banana")
print("bananaIndex: \(bananaIndex)")
let mangoIndex = findString(fruits, "mango")
print("mangoIndex: \(mangoIndex)")
// bananaIndex: 1
// mangoIndex: nil
Optional<Type>
型の値からType
型の値を取り出すことを、アンラップするという。
Optional
型の値をアンラップするには、値のうしろに"!
"を付ける。
アンラップする場合には、値が確かに存在すること(nil
ではないこと)を確認しなければならない。
let possibleKiwiIndex = find(fruits, "kiwi") // possibleKiwiIndexはInt?型
if possibleKiwiIndex != nil {
let kiwiIndex = possibleKiwiIndex!
// possibleKiwiIndexがnilでないことを確認したので、安全にアンラップできる
// kiwiIndexはInt型であり、nilでないことが保証されている
print("キウイは\(kiwiIndex)番目に見つかりました")
} else {
print("キウイは見つかりませんでした")
}
if文とwhile文ではOptional束縛(optional binding)を記述できる。
上のコードをOptional束縛を利用して書くと次のようになる。
if let kiwiIndex = find(fruits, "kiwi") {
// もしfindからのInt?型の戻り値に値が入っていれば、kiwiIndexにその値を代入する
print("キウイは\(kiwiIndex)番目に見つかりました")
} else {
print("キウイは見つかりませんでした")
}
while文で使用するOptional束縛の例を下に示す。
// 1行ずつ読み出す関数のダミー
// 呼び出すごとに行を進めていき、最後の行まで到達するとnilを返す
let readLine: () -> String? = {
let lines = [
"Lorem ipsum dolor sit amet,",
"consectetur adipisicing elit,",
"sed do eiusmod tempor incididunt",
"ut labore et dolore magna aliqua."
]
var idx = -1
return { return ++idx < lines.count ? lines[idx] : nil }
}()
// readLineの戻り値に値が存在していれば
// lineに代入してブロックの中を実行する
while let line = readLine() {
print(line)
}
// Lorem ipsum dolor sit amet,
// consectetur adipisicing elit,
// sed do eiusmod tempor incididunt
// ut labore et dolore magna aliqua.
関数とクロージャ
Swiftにおける関数とクロージャは第一級関数であり、普通のオブジェクトと同じように変数に入れたり、引数として受けとったり、戻り値として返すことができる。
関数
関数の定義はfunc
キーワードを使って以下のように行う。
// 体重と身長からBMI値を計算する関数
func calcBMI(weight: Double, height: Double) -> Double {
return weight / (height * height) * 10000
}
calcBMI(60, 160) // 23.4375
上の関数は(Double, Double) -> Double
型のオブジェクトとして扱うことができる。
let myFunc: (Double, Double) -> Double = calcBMI
myFunc(60, 160) // 23.4375
関数の引数はデフォルトで定数であり、関数内でその値を変更することはできない。しかしvar
キーワードを付けて引数を宣言すると、その引数は関数内で値を変更できるようになる。なお、デフォルト(let
)、var
どちらで宣言した場合も、引数は値渡し(参照型の引数は参照の値渡し)である。var
で宣言した引数に別の値を再代入しても、呼び出し元の変数の値は変わらない。
func someFunction(a: Int, let b: Int, var c: Int) {
// a = 99 // 引数はデフォルトで定数であり、再代入不可
// b = 99 // 明示的に let を指定してもよい
c = 99 // var で引数を宣言すれば、関数内で値を変更できる
}
外部引数名
関数の引数には外部引数名(external parameter name)を指定することができる。対して関数内部のみで使用できる引数名はローカル引数名(local parameter names)と呼ぶ。外部引数名が指定された関数を呼び出すときは、明示的にその名前を記述しなければならない。外部引数名を使うことによって、関数を呼び出す側のコードで引数の意味を明確にすることができる。
func calcBMI(weight w: Double, height h: Double) -> Double {
return w / (h * h) * 10000
}
calcBMI(weight: 60, height: 160) // 23.4375
外部引数名とローカル引数名が同じならば、引数名の前に"#
"を付けた短縮形を使うことができる。
func calcBMI(#weight: Double, #height: Double) -> Double {
return weight / (height * height) * 10000
}
calcBMI(weight: 60, height: 160) // 23.4375
関数のネスト
関数の中で関数を定義することが可能である。
func calcBMI(weightInPounds w: Double, heightInInches h: Double) -> Double {
func poundsToKilograms(val: Double) -> Double { return val * 0.45359237 }
func inchesToMeters(val: Double) -> Double { return val * 2.54 }
let convertedWeight = poundsToKilograms(w)
let convertedHeight = inchesToMeters(h)
return convertedWeight / (convertedHeight * convertedHeight) * 10000
}
calcBMI(weightInPounds: 130, heightInInches: 65) // 21.6329101427434
可変長引数
引数の型に続けて"...
"(ピリオド3つ)を記述すると可変長引数(variadic parameter)となる。可変長引数は関数内では配列として扱うことができる。可変長引数にできるのは一番後ろの引数だけである。
func maxInt(first: Int, rest: Int...) -> Int {
var max = first
// Int型の可変長引数restは、Int型の配列として扱える
for num in rest {
if max < num {
max = num
}
}
return max
}
maxInt(7, 3, 5, 9, 2) // 9
maxInt(6) // 6
デフォルト引数
引数には個別にデフォルト値を設定でき、デフォルト値が設定された引数は呼び出す際に省略することが可能となる。
デフォルト値が設定された引数は自動的に名前付き引数となる。
func sayHello(name: String = "world") {
print("Hello, \(name)!")
}
sayHello() // Hello, world!
sayHello(name: "Swift") // Hello, Swift!
in-out引数
inout
キーワードを付けて引数を宣言すると、引数に値のコピーが渡り、関数を実行した結果の値がコピーされて呼び出し元に書き戻される、いわゆる値呼びの結果返し(call-by-value-result)となる。Swiftではこれをin-out引数と呼ぶ。呼び出し側ではin-out引数に渡す変数名の前に&
を付ける。なおin-out引数にデフォルト値を設定することはできず、また定数やリテラルなどの値を変更できないものを渡すこともできない。in-out引数はコンパイラによって参照呼び(call-by-reference)に最適化される場合がある。Swift3.0から変更するのはパラメーターではなく型であること、パラメーターラベルの変更との表記上の類似性を避けるということから型の前に置かれるように変更された。
//Swift2まで
func swapInts(inout a: Int, inout b: Int) {
let tmp = a
a = b
b = tmp
}
//Swift3から
func swapInts(a: inout Int, b: inout Int) {
let tmp = a
a = b
b = tmp
}
var x = 42, y = 99
swapInts(&x, &y)
print("x = \(x), y = \(y)") // x = 99, y = 42
var arr = [1,2,3]
swapInts(&arr[0], &arr[2])
print(arr) // [3, 2, 1]
クロージャ
Swiftでは中括弧{}
で囲まれたコードブロックは事実上全てクロージャとなる。
このとき、引数・戻り値と、本体の処理の間に「in
」を書くことで、この2つの部分を区別する。
以下の例は、整数型引数number
を持ち、整数の結果(ここではnumber
の3倍の値)を返すクロージャが使われている。
配列のメソッドmap
は、関数もしくはクロージャを引数に持ち、
配列要素の一つ一つにこの関数またはクロージャを適用する機能を持つ。
この例では、members
の配列要素がそれぞれ3倍され、[1, 2, 3]
が[3, 6, 9]
となる。
[1, 2, 3].map({
(number: Int) -> Int in
let result = 3 * number
return result
})
クロージャを書く場合に、いくつかのオプションがある。
delegateのコールバックのようにクロージャの型が推論可能な場合、
引数の型やクロージャの戻り値の型のいずれかもしくは両方の表記を省略することができる。
例えば上記の例では、引数も戻り値も配列の要素(型はInt
)であるので、下のように型の指定を省略できる。
[1, 2, 3].map({
number in
let result = 3 * number
return result
})
また、クロージャの本体に1文しかない場合は、その値を返すものと考えられ、
以下のように、return
文に関係する記述を省略する事ができる。
[1, 2, 3].map({ number in 3 * number })
さらに、引数を文字ではなく数字で表す事でさらに短く表現できる。
クロージャに渡される引数は、順番に$0
、$1
、…で示すこともできる。そこで、以下のようにできる。
[1, 2, 3].map({ 3 * $0 })
また、関数の最後の引数がクロージャであった場合、それを括弧の外に出してもよい。
[1, 2, 3].map(){ 3 * $0 }
さらに、この場合、空の括弧、()
は省略可能である。
[1, 2, 3].map{ 3 * $0 }
もう一つ別の例を示す。
sorted
関数は、第1引数に配列を、第2引数に並べ替えルールを示す関数もしくはクロージャを書くことで、
配列の要素を並べ替えた新しい配列を返す。
並べ替えルールは、隣接する要素がルールに従っている場合にtrue
を返す関数である。
配列要素がInt
である場合、たとえば、以下のルール関数により、
[1, 5, 3, 12, 2]
という整数配列を昇順に並べ替えることができる。
func rule(a:Int, b:Int) -> Bool {
return a < b
}
sorted([1, 5, 3, 12, 2],rule)
sorted
関数は[1, 2, 3, 5, 12]
という配列を返す。ルール関数の部分をクロージャで書くと以下のようになる。
sorted([1, 5, 3, 12, 2], {(a:Int, b:Int) -> Bool in return a < b })
これも、上記のmap
メソッドの例と同様に、以下のように簡潔に記述する事ができる。
sorted([1, 5, 3, 12, 2]) { return $0 < $1 }
列挙型
タイプセーフの共通型を列挙する
enum SomeEnumeration {
//列挙の定義がここに入る
}
//例 方角を定義
enum CompassPoint {
case North
case South
case East
case West
}
var directionToHead = CompassPoint.West
directionToHead = .East
enum Planet {
case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}
クラスと構造体 /class struct
Swiftの構造体はC言語の構造体から大きく強化されており、クラスと同様にメソッドやコンピューテッド・プロパティを実装することができる。
クラスと構造体の宣言は、それぞれclass
キーワードとstruct
キーワードを使って次のように行う。
class クラス名 {
// 実装…
}
struct 構造体名 {
// 実装…
}
クラスは参照型/ reference typeで、アサインされる時には参照ポインタが渡される。 構造体は値型/ value typeでletやvarでアサインされる時にコピーされる。これに属する物としては、 String Array Dictionary が有る。
プロパティ
ストアド・プロパティ(Stored Property)
プロパティはlet
またはvar
を使って、ローカル定数/変数と同じように定義できる。プロパティにアクセスする時はドット記法により行う。
ストアド・プロパティは他言語のインスタンス変数に近いが、遅延初期化や値の監視(後述)など、より高機能である。
class SomeClass {
var val1 = 0 // 宣言時に初期化する場合は、型推論により型を省略できる
var val2: Int // 宣言時ではなくイニシャライザで初期化する場合は、型を明示する
let val3 = 0
let val4: Int
init() {
// 宣言時に初期化せずにイニシャライザ内で初期化してもよい
val2 = 0
val4 = 0
}
}
let obj = SomeClass()
print(obj.val1) // プロパティにはドット記法でアクセスする
obj.val2 = 1
//obj.val3 = 1 // letで宣言したプロパティには再代入不可
ストアド・プロパティは定義の前にlazy
修飾子を付けることで、初めてアクセスされる時まで初期化の実行を遅らせることができる。
class HeavyClass {
init() { print("HeavyClassをインスタンス化") }
func method() { print("HeavyClassのメソッド") }
}
class SomeClass {
lazy var heavy: HeavyClass = HeavyClass()
init() { print("SomeClassをインスタンス化") }
func method() { print("SomeClassのメソッド") }
}
var obj = SomeClass()
obj.method()
obj.heavy.method() // ←ここで初めてheavyプロパティが初期化される
// 実行結果:
// SomeClassをインスタンス化
// SomeClassのメソッド
// HeavyClassをインスタンス化
// HeavyClassのメソッド
遅延ストアド・プロパティ(Lazy Stored Property)
初期値は最初に使われる時まで計算されない。
コンピューテッド・プロパティ(Computed Property)
ゲッター/セッターを介してアクセスするプロパティを定義できる。
他言語の一般的なプロパティに近い。
class Square { // 正方形クラス
var sideLength: Double // 一辺の長さ(ストアド・プロパティ)
var area: Double { // 面積(コンピューテッド・プロパティ)
get { // ゲッター
return sideLength * sideLength
}
set { // セッター
sideLength = sqrt(newValue)
}
}
init(sideLength: Double) {
self.sideLength = sideLength
}
}
let sq = Square(sideLength: 5)
println(sq.area) // 25.0
sq.area = 36
println(sq.sideLength) // 6.0
ゲッターのみを定義すれば読み出し専用のコンピューテッド・プロパティとなる。この場合、get
キーワードを省略できる。
class Person {
var firstname, lastname: String
var fullname: String { return firstname + " " + lastname }
init(firstname: String, lastname: String) {
self.firstname = firstname
self.lastname = lastname
}
}
var author = Person(firstname: "Jonathan", lastname: "Swift")
println(author.fullname) // Jonathan Swift
プロパティ値の監視 (Property Observers)
プロパティの値がセットされる時に常に動くように監視している。
willset 値が格納される直前に呼ばれる。
didset 値が格納された直後に呼ばれる
型プロパティ
普通のプロパティが個々のインスタンスに属するのに対して、同じクラスの全てのインスタンスで共有するプロパティを、型プロパティ(type property)という。他の言語でいうクラス変数に近い。
型プロパティはクラスではclass
キーワードを、構造体ではstatic
キーワードを使って宣言する。
class MyClass {
class var computedTypeProperty: Int { … }
}
struct MyStruct {
static var storedTypeProperty = 42
static var computedTypeProperty: Int { … }
}
メソッド
メソッドは特定の型に結びつけられた関数である。 クラス、構造体、列挙型のすべては、 そのインスタンスと共に実行するタスクや機能をカプセル化した インスタンスメソッドを定義することができる。 クラス、構造体、列挙型は、 型そのものと一緒に実行する 型メソッド(type method)を定義することができる。 型メソッドは他言語のクラスメソッドに近い。 Swiftにおいて構造体と列挙型がメソッドを定義できることは、C言語やObjective-Cとの大きな違いである。 Objective-Cにおいてはクラスがメソッドを定義できる唯一の型であった。 Swiftにおいては、クラス、構造代、列挙型でメソッドを定義できる。
インスタンスメソッド
インスタンスメソッドは、特定のクラス、構造体、列挙型のインスタンスに属する関数である。
インスタンスメソッドは、
インスタンスプロパティを読み書きする機能や、
インスタンスの目的に関した機能を提供する。
インスタンスメソッドは、関数とまったく同一の文法で記述される。
インスタンスメソッドは、その型のすべてのインスタンスメソッドとプロパティにアクセスできる。 インスタンスメソッドは、それが記述されている型のインスタンスに対してのみ呼び出すことができる。 インスタンスの指定無しでメソッドだけを単独に呼び出すことはできない。
以下に、整数型の長さの辺を持つ三角形のクラスを定義している(あまり実用的ではないが)。
ここでは、3辺の長さを設定するインスタンスメソッドsetSides
と、
3辺の合計(perimeter
)を返すインスタンスメソッドperimeter
を定義している。
インスタンス内の変数には変数名だけでアクセス可能であるが、
とくに区別したい場合は自分自身を表すself
を使用する。
以下の例では、setSides
メソッドで、引数のa
、b
、c
とインスタンス変数のa
、b
、c
を区別するためにself
を使用している。
class Triangle {
var a=0, b=0, c=0
func setSides(a:Int, b:Int, c:Int) {
self.a = a; self.b = b; self.c = c
}
func perimeter() -> Int {
return a + b + c
}
}
以下では、3辺に3、 4、 5を設定し、周囲長12を表示している。
他言語に対して特徴的な点は、
引数が2個以上のメソッドの呼び出し方法である(例ではsetSides
)。
最初の引数は値のみを記述するが、
2番目以下の引数では、
メソッド定義で仮引数として書かれた変数名にコロンを付けてラベル付けする。
ここでは、第2、3引数に、b:
とc:
のラベルを付けている。
let myTriangle = Triangle()
// 3辺の長さを設定する
myTriangle.setSides( 3, b:4, c:5 )
// 周囲の長さを表示する(12が表示される)
println( myTriangle.perimeter() )
メソッドの2番目以下の引数にラベルを付記する方式は、 セレクタを使用したメッセージ渡しを行うObjective-Cとの整合性を高めるためである。 Smalltalkの流れを汲むObjective-Cでは、これに相当するメソッドは
[ myTriangle setSideA:3 B:4 C:5 ]
というように、setSideA:
、B:
、C:
の3個のセレクタで構成されるメッセージで呼び出されるのが通例である。
最初のセレクタをメソッド名に、以降のセレクタをラベルにすることで、
Objective-Cとの共存や、Objective-Cで構築されたフレームワークへのアクセスを実現している。
型メソッド
インスタンスメソッドがある型のインスタンス上で呼ばれるのに対し、型それ自身の上で呼ばれる型メソッドを定義できる。
型メソッドは、クラスの場合はfuncキーワードの前にclassを書いて定義する。
構造体や列挙型の場合はfuncキーワードの前にstaticを書いて定義する。
class SomeClass {
class func someTypeMethod() { // <<****** 型メソッド定義
// 型メソッドの中身
}
}
SomeClass.someTypeMethod() // <<****** 型メソッド呼び出し
struct LevelTracker {
static var highestUnlockedLevel = 1
static func unlockLevel(level: Int) { // <<****** 型メソッド定義
if level > highestUnlockedLevel { highestUnlockedLevel = level }
}
static func levelIsUnlocked(level: Int) -> Bool { // <<****** 型メソッド定義
return level <= highestUnlockedLevel
}
var currentLevel = 1
mutating func advanceToLevel(level: Int) -> Bool {
if LevelTracker.levelIsUnlocked(level) {
currentLevel = level
return true
} else {
return false
}
}
}
イニシャライザ(Initializer)
イニシャライザは、そのクラスのオブジェクトがインスタンス化される際に呼ばれる処理を記述する。他の言語で言うところのコンストラクタに相当する。
init
に続けてブロックを記述する。メソッドの定義に似ているがfunc
キーワードや戻り値の型、return
文は書かない。
class SomeClass {
init(引数…) {
// 初期化処理…
}
}
ひとつのクラスに複数のイニシャライザを記述することができる。
初期化が必要なプロパティのすべてが初期化されることを保証するイニシャライザのことを、指定イニシャライザ(designated initializer)と呼ぶ。さらに、クラスを継承している場合は、指定イニシャライザはスーパークラスのイニシャライザを呼び出さなければならない。
それに対して、プロパティの初期化を他のイニシャライザに委譲するイニシャライザを、コンビニエンス・イニシャライザ(convenience initializer)と呼ぶ。
コンビニエンス・イニシャライザはinit
キーワードの前にconvenience
キーワードを置く。
コンビニエンス・イニシャライザの中からコンビニエンス・イニシャライザを呼び出すことは可能だが、最終的には指定イニシャライザが呼び出されるようにしなければならない。
class Color {
var red, green, blue: Int // 宣言時に初期化していないので、指定イニシャライザでの初期化が必要。
// 指定イニシャライザ
init(red: Int, green: Int, blue: Int) {
// 全ての初期化が必要なプロパティを初期化する。
self.red = red
self.green = green
self.blue = blue
}
convenience init(white: Int) {
// コンビニエンス → 指定イニシャライザ
self.init(red: white, green: white, blue: white)
}
convenience init() {
// コンビニエンス → コンビニエンス → 指定イニシャライザ
self.init(white: 0)
}
}
var brown = Color(red: 0xA5, green: 0x2A, blue: 0x2A)
var gray = Color(white: 0x80)
var black = Color()
Objective-Cでは指定イニシャライザは規約でしかなかったが[11]、Swiftでは文法として厳密に検査される。
なお、Objective-Cにおいてコンビニエンス・コンストラクタと呼ばれているものは、例えばNSString
のインスタンス生成に[[[NSString alloc] initWithFormat:...] autorelease]
と呼ぶところを、[NSString stringWithFormat:...]
のように短く便利に呼べるようにしたもののことであり、Swiftのコンビニエンス・イニシャライザとは別物である。
引数の無いイニシャライザはデフォルト・イニシャライザ(default initializer)と呼ばれ、明示的に定義されていなくても自動で生成される。
class Note {
var title = "No Title" // 初期化済
var body: String? // Optional型なので暗黙的にnilで初期化される
}
var newNote = Note()
構造体はデフォルト・イニシャライザに加えて、メンバをすべて引数に受けとるイニシャライザ(memberwise initializer)が自動で生成される。
struct Point{
var x = 0.0, y = 0.0
}
var p1 = Point() // {x 0.0, y 0.0}
var p2 = Point(x: 10, y: 50) // {x 10.0, y 50.0}
デイニシャライザ(deinitializer)
デイニシャライザ(deinitializer)にはインスタンスが破棄される際に実行されるべき処理を記述する(開いたファイルを閉じるなど)。他言語のデストラクタに相当する。
deinit
に続けて処理ブロックを記述する。引数、戻り値はともに無い。
イニシャライザと違いデイニシャライザは自動で呼びだされるものであり、自分で呼び出すことはできない。
デイニシャライザはクラスにだけ存在し、構造体には無い。
class SomeClass {
deinit {
// 後処理…
}
}
継承
クラス名の後に":
"を挟んでスーパークラスの名前を記述する。
Swiftは多重継承はサポートしない。
メソッドをオーバーライドする場合は、メソッドの定義にoverride
属性の指定が必須である。これは意図しないオーバーライドを予防するためである。
class クラス名: スーパークラス名 {
}
なお、Objective-CではNSObjectが全てのクラスの共通の基底クラスであったが、Swiftにはそのような全てのクラスに共通の基底クラスというものは存在しない。
プロトコル
Objective-Cのプロトコルと同様の概念であり、Java言語などのインタフェースに相当する機能である。
拡張|エクステンション
Swiftのクラスはエクステンション(extension)を用いて既存のクラスを拡張することができる。これはObjective-Cのカテゴリとよく似ている。
ジェネリック
ジェネリック(総称性)コードは、柔軟で再使用可能な関数や型を可能にする。
ジェネリックはSwiftの最も強力な機能の一つでSwiftの標準ライブラリは殆どジェネリックコードで作られている。 配列や辞書型もジェネリック集合である。 IntでもStringでも色んな型を入れる事が出来る。
ジェネリックにすることにより一つの定義であらゆるタイプを取扱う事が出来るようになる。
ジェネリックタイプ
自分用のジェネリックタイプを定義できる。 それには、カスタムClass、構造/structure、列挙/enumeration 等が配列や辞書と同じ様にどんなタイプとでも動く。
struct Stack<T> {
var items = [T]()
mutating func push(item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
}
var stackOfStrings = Stack<String>()
stackOfStrings.push("一")
stackOfStrings.push("二")
// スタックに2つの文字列が入った。
ジェネリック関数
下の例は、自由な型の2つの値を交換する関数である。 Tは架空の型名で実際にはどんな型でも動く。
func swapTwoValues<T>(inout a: T, inout b: T) {
let temporaryA = a ; a = b ; b = temporaryA
}
var someInt = 3 , anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
var someString = "hello" , anotherString = "world"
swapTwoValues(&someString, &anotherString)
型束縛
swapTwoValues関数と Stackタイプはどんな型でも動く。 しかしジェネリック関数やジェネリックタイプで使われるとき、型束縛をして制限した方が使いやすい事がしばしばある。
型パラメータリストの一部として、型パラメータの名前の後ろにコロン:で区切って単一クラスやプロトコル束縛を書いて型束縛とする。
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// 関数本体
}
型束縛例
func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? {/*略*/}
//
let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3)
let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")
関連タイプ (typealias)
プロトコルを定義する時にいくつかの関連タイプ(別名:alias)を定義した方が使いやすい時が有る。 typealias を使って別名を定義する。
関連タイプ例
Container プロトコルが ItemTypeと言う関連タイプを持つ例
protocol Container {
typealias ItemType
mutating func append(item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
Where句
型束縛にwhereで更なる条件を付ける
func allItemsMatch<
C1: Container, C2: Container
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
(someContainer: C1, anotherContainer: C2) -> Bool {
//略
}
その他
演算子オーバーロード
Swiftでは任意の型に対して、既存の演算子をオーバーロードして独自の振る舞いを定義することができる。
演算子オーバーロードは通常の関数定義とほとんど同じ形式で定義するが、前置演算子をオーバーロードする場合はprefix
修飾子を、後置演算子の場合はpostfix
修飾子を、それぞれfunc
キーワードの前に付ける。複合代入演算子をオーバーロードする場合は、左辺の値を変更するため左辺の引数をin-out引数とする。
なお = -> . // /* */
は予約されておりオーバーロードしたりカスタム演算子(後述)として定義することはできない。
struct Vector2D {
var x = 0.0
var y = 0.0
}
func + (lhs: Vector2D, rhs: Vector2D) -> Vector2D {
return Vector2D(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
}
prefix func - (rhs: Vector2D) -> Vector2D {
return Vector2D(x: -rhs.x, y: -rhs.y)
}
func += (inout lhs: Vector2D, rhs: Vector2D) {
lhs = lhs + rhs
}
prefix func ++ (inout rhs: Vector2D) -> Vector2D {
rhs += Vector2D(x: 1.0, y: 1.0)
return rhs
}
let vec1 = Vector2D(x: 2.0, y: 3.0)
let vec2 = Vector2D(x: 5.0, y: 1.0)
let vec3 = vec1 + vec2 // {x 7.0, y 4.0}
var vec4 = -vec1 // {x -2.0, y -3.0}
vec4 += vec2 // {x 3.0, y -2.0}
++vec4 // {x 4.0, y -1.0}
カスタム演算子
Swift標準の定義済み演算子(+
、*
、%
など)の他に、全く新しい演算子を定義することができる。新しく定義したカスタム演算子は、標準の演算子と同じように演算子オーバーロードを実装できる。
カスタム演算子の定義はoperator
キーワードで行う。さらに、前置演算子ならprefix
、後置演算子ならpostfix
、中置演算子ならinfix
修飾子をoperator
キーワードの前につける。
演算子名に使用できる記号は + - * / % = < > ! & | ^ ~ .
及び数学記号等の一部のユニコード文字の組み合わせに限られる。
// 前置カスタム演算子
prefix operator 演算子名 {}
// 後置カスタム演算子
postfix operator 演算子名 {}
// 中置カスタム演算子
infix operator 演算子名 { precedence 優先度 associativity 結合性 }
中置カスタム演算子を定義する場合、任意でその優先度と結合性を定義できる。
優先度の定義はprecedence
キーワードに続けて、0から255の範囲の整数値で設定する。優先度の定義を省略した場合のデフォルト値は100
である。
結合性の定義はassociativity
キーワードに続けて、左結合ならleft
キーワード、右結合ならright
キーワード、非結合ならnone
キーワードを記述する。結合性の定義を省略した場合のデフォルト値はnone
(非結合)である。
// 中置演算子 ** を定義する
infix operator ** { precedence 155 associativity right }
// ** 演算子を Double型のべき乗として実装する
func ** (lhs: Double, rhs: Double) -> Double {
return pow(lhs, rhs)
}
2 ** 3 // 8.0(2の3乗)
0.5 * 2 ** 3 ** 2 // 256.0
// **演算子は *演算子よりも優先順位が高く、右結合として定義したので
// この式は 0.5 * (2 ** (3 ** 2)) の意味となる
カリー化関数
カリー化関数(Curried Function)は、複数のパラメータを持つ関数 f(a,b)を1つのパラメータを持つ関数 F(a)->g(b) に分解して再定義したF関数の事を言う。
カリー化した後の評価は右から左へ行われる。 カリー化は一つの引数のみを取る複数の関数のラムダ計算等に必要な機能となる
func addTwoInts(a: Int, b: Int) -> Int {
return a + b
}
func addTwoIntsCurried(a: Int) -> (Int -> Int) {
func addTheOtherInt(b: Int) -> Int {
return a + b
}
return addTheOtherInt
}
addTwoInts(4,5) //9
addTwoIntsCurried(4)(5) //9
let plusOne = addTwoIntsCurried(1) //plusOne は Int->Int の関数
plusOne(10) //11
メモリ管理(Automatic Reference Counting)
Swiftでは自動参照カウント(Automatic Reference Counting)ARC でアプリが使うメモリを追跡管理をする。
ARCはクラスインスタンスが使われなくなった時に自動的にそれが使っていたメモリを開放する。
相互運用性
SwiftはcocoaやObjective-Cをシームレスに使えるように設計されている。[12]SwiftとObjective-Cの間はどちらのAPIからでもお互いに使う事が出来る。
モジュールとしてアクセス可能などんなObjective-Cフレームワーク(又はCライブラリ)でもSwiftに直接インポートできる。
import Foundation
初期化の例
- OBJECTIVE-C
UITableView *myTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
- Swift
let myTableView: UITableView = UITableView(frame: CGRectZero, style: .Grouped)
C言語APIとの互換
SwiftからLibcを呼ぶ例
puts("Hello from libc")
let fd = open("/tmp/scratch.txt", O_WRONLY|O_CREAT, 0o666)
if fd < 0 {
perror("could not open /tmp/scratch.txt")
} else {
let text = "Hello World"
write(fd, text, strlen(text))
close(fd)
}
出典
- ↑ Williams, Owen (2014年6月2日). “Apple announces Swift, a new programming language for iOS”. The Next Web. . 2014閲覧.
- ↑ “Introducing Swift”. Apple. . 2014閲覧.
- ↑ Swift Playgrounds、App Storeで提供開始
- ↑ Swift Playgrounds、さらに5か国語に対応
- ↑ Swift 3.1 Released! - swoft.org (2017年3月27日)
- ↑ [swift-evolution Update on the Swift Project Lead] - Chris Lattner (2017年1月10日)
- ↑ Swift 4.0 Released! - Ted Kremenek (2017年9月19日)
- ↑ “「Xcode」をMac App Storeで” (ja-JP). Mac App Store. . 2018閲覧.
- ↑ Xcode 10 includes Swift 4.2, which compiles your software more quickly, helps you deliver faster apps, and generates even smaller binaries. “Xcode 10” (en-US). Developer. . 2018閲覧.
- ↑ Accepted proposals for Swift 3.0
- ↑ “Cocoa向け コーディングガイドライン (PDF)”. Apple Inc.. . 2014閲覧.
- ↑ “Using Swift with Cocoa and Objective-C (HTML)”. Apple Inc.. . 2014閲覧.
外部リンク
- Swift - Apple Developer
- The Swift Programming Language (Swift 4), Apple Inc. (2017年9月19日)
- Using Swift with Cocoa and Objective-C, Apple Inc. (2014)
- iOS Standard Library, Apple Inc. (2014)
- Swift 公式BLOG, Apple Inc. (2014)
- IBM Swift Sandbox
- Swift Playgrounds - iPadで本格的なコードを学べます。圧倒的に楽しい方法で。