ドキュメントから漏れている予約語もあるため、下記情報を統合してカウントしています。
- The Swift Programming Language (Swift 5) Lexical Structure
- The Swift Programming Language (Swift 5) Summary of the Grammar
- https://github.com/apple/swift/blob/main/utils/gyb_syntax_support/Token.py
- https://github.com/apple/swift/tree/main/lib/Parse
宣言 (declarations)
型、変数、定数、メソッド、関数などの宣言部分で使用する予約語
actor
[Swift 5.5~]
Swift の型の種類の一つである Actor 型を宣言します。
actor SampleActor {
var mutableValue = "mutable"
let constValue = "const"
func defaultFunc() {}
nonisolated func nonisolatedFunc() {}
nonisolated var nonisolatedComputedProperty: String {
"computedProperty getter"
}
}
Actor を使うことで、複数スレッドからの変更可能データに対する同時アクセスによって発生するデータの破損(data race)を防ぐコードをコンパイラのサポートを受けながら書けるようになります。
Actor の概要は Swift LanguageGuide > Concurrency > Actors に記載されています。実際のアプリ開発での使い所のイメージを掴んでみたい場合は WWDC21 Swift concurrency: Update a sample app が分かりやすくてオススメです。
詳細
Actor の特徴は並列処理の実行環境下でのデータの保護です。(より厳密に言うなら、actor の instance を通すことで、隔離された状態に同期的にアクセスする機能を提供するものです。)
Actor の持つ変更可能なデータや method に対して同時に複数のタスクからアクセスされないように自動的に制御されます。他のタスクからアクセスされている場合はアクセス可能になるまで待機します。そのため、アクセスする場所では await
でマークしておく必要があります。
await sampleActor.mutatingValue
await sampleActor.defaultFunc()
変更不可なデータである定数や nonisolated
が付与されている method/computed property は data race が発生しないことが保証されているため複数タスクから同時にアクセスしても問題ありません。そのため、 await
を必要とせずにアクセスできます。この状態を「隔離されていない(non-isolated)」と表現します。これについては nonisolated の項目 に記載しています。
sampleActor.constValue
sampleActor.nonisolatedFunc()
sampleActor.computedProperty
関連: nonisolated, await
async
[Swift 5.5~]
1. function 宣言部の async
非同期の function を表します。
func listPhotos(inGallery name: String) async -> [String] {
let result = // ... some asynchronous networking code ...
return result
}
DB 操作やネットワーク処理などの非同期の処理を待つ、つまり、途中で関数内の処理を中断・再開するような関数を表します。非同期関数を呼ぶには await
を使って結果を待つ(suspension point)ことを明示します。
let photoNames = await listPhotos(inGallery: "Summer Vacation")
2. async let
非同期関数の結果を受け取る変数を宣言します。let thumbnail = await downloadImage(...)
とする場合と違い、実際に値を使う部分に到達するまで待機しません。
下記の例では thumbnail と authorImage の download が並列で実行され、値が使われる箇所(let photos = ...
)で download の終了を待ちます。
async let thumbnail = downloadImage(url: bookImageUrl) // download 開始
async let authorImage = downloadImage(url: authorImageUrl) // download 開始
let photos = await [thumbnail, authorImage] // thumbnail と authorImage の download が終わるまで待機
関連: await
await
[Swift 5.5~]
1. await
+ fuction/method
非同期関数の終了を待つ部分(suspension point)を表します。
// download が終わるまで次の処理に進まない。
// (thread を一時的に解放して他の待機中のタスクが実行される)
let photo = await downloadPhoto(named: name)
show(photo)
2. for await-in
loop
AsyncSequence に対して loop を回す場合は sequence から要素が渡されるまで待機します。
for await i in Counter(howHigh: 10) {
print(i, terminator: " ")
}
class
場所によって意味が異なります。
- クラスの宣言や、メソッドやプロパティの前に指定することでクラスメソッド、クラスプロパティの宣言をします。
class Sample {
class func f() {}
class var classProperty: String {
"classProperty"
}
}
- プロトコルの適用先をクラスだけに制限したい場合(deprecated)
後方互換のためこの用途で機能しますが Swift4 からは AnyObject
を使用するのが正しいです。
また、Xcode12.5 から AnyObject
に置き換えるように警告が出ます。
// warning: using 'class' keyword for protocol inheritance is deprecated; use 'AnyObject' instead
protocol SampleProtocol: class {}
class Sample: SampleProtocol {}
参考:
- @YOCKOW さんからのコメント
- Github Apple/swift PR: [Sema] Adding deprecation warning for protocol inheritance 'class' syntax #34885
deinit
デストラクタの宣言をします。これは class でのみ有効です。
スーパークラスの deinit はサブクラスの deinit 実行後に自動的に呼ばれます。
class SuperClass {
deinit { print("Super Class's deinit is called") }
}
class SubClass: SuperClass {
deinit { print("Sub Class's deinit is called") }
}
var subClass: SubClass? = SubClass()
subClass = nil
Sub Class's deinit is called
Super Class's deinit is called
enum
列挙型の宣言をします。
enum SampleEnum {
case a
case b(Int)
}
extension
定義済みのクラス、構造体、プロトコル、列挙型に対して function または computed property を追加します。
extension Array {
func f() {}
}
let intArray = [1]
intArray.f()
protocol の適合の追加や
extension SampleClass: HogeDelegate {
func f() {}
}
where
で適応する条件を指定できます。
extension Array where Element == Int {
func funcForIntArray() {}
}
intArray.funcForIntArray()
fileprivate
アクセス修飾子の一種です。
同じファイル内からのアクセスが可能です。
関連: public, private, internal, open
参考: The Swift Programming Language # Access Control
func
function の宣言をします。
import
モジュールを読み込みます。
init
コンストラクタを宣言します。
inout
intout 指定された引数として渡された変数は、function 内での変更が適応されます。(表面的には参照渡しに似た動作になります)
具体的には下記の動作をします。
- function の呼び出し時に、引数として与えられた値がコピーされ、そのコピーが function の中で使われます。
- function の終了時に引数として与えられた変数にコピーが代入されます。
class SampleClass {}
func inoutSampleForClass(sampleClass: inout SampleClass?) {
sampleClass = nil
}
func withoutInoutSampleForClass(var sampleClass: SampleClass?) {
sampleClass = nil
}
var sampleClass: SampleClass? = SampleClass()
// inout なし
withoutInoutSampleForClass(sampleClass)
sampleClass // => SampleClass のインスタンス
// inout あり
inoutSampleForClass(&sampleClass)
sampleClass // => nil
func inoutSample(a: inout Int) {
a = 100
}
func withoutInoutSample(var a: Int) {
a = 100
}
var varInt = 1
// inout なし
withoutInoutSample(varInt)
varInt // => 1
// inout あり
inoutSample(&varInt)
varInt // => 100
internal
アクセス修飾子の一種です。
同じモジュール内からアクセスできます。
アクセス修飾子をつけなかった場合、デフォルトで internal
となります。
関連: public, private, open, fileprivate
参考: The Swift Programming Language (Swift 3) # Access Control
let
定数を宣言します。
関連: var
open
アクセス修飾子の一種です。アクセス制限の種類の中で最もアクセス制限の緩い指定です。
外のモジュールからのアクセス、継承・オーバーライドが可能です。
性質上、この制限を指定できるのは class または オーバーライド可能な class member のみです。
public との違いは、継承・オーバーライドの許可範囲 (外のモジュールからの継承・オーバーライド可能性) です。
関連: public, private, internal, fileprivate
参考: The Swift Programming Language # Access Control
operator
独自の演算子を定義します。
演算子として採用できる文字については規定があります。詳細は iOS Developer Library の Language Reference > Lexical Structure > Operators をご覧ください。
関連: prefix, postfix, infix
prefix operator ☁
prefix func ☁ (a: inout Int) -> Int {
a *= a
return a
}
postfix operator *** // 複数文字列も可能
postfix func *** (a: inout Int) -> Int {
a *= a
return a
}
infix operator ☁
func ☁ (left: Int, right: Int) -> Int {
return left + right
}
var hoge = 2
☁hoge // => 4
hoge*** // => 16
1 ☁ 2 // => 3
precedencegroup
左右に被演算子をとる演算子の優先度グループを定義します。
higherThan で、どのグループより優先度が高いか指定できます。lowerThan では、他のモジュールのどのグループより優先度が低いか指定できます。associativity と assignment も指定できます。
infix operator <#operator name#> : <#precedence group name#>
precedencegroup GroupA {
associativity: none
}
precedencegroup GroupB {
higherThan: GroupA
associativity: none
}
infix operator ***: GroupA
infix operator +++: GroupB
func *** (left: Int, right: Int) -> Int {
return left * right
}
func +++ (left: Int, right: Int) -> Int {
return left + right
}
1 +++ 1 *** 0 // => 0
関連: operator, associativity, assignment
private
アクセス修飾子の一種です (型、変数、定数、function の公開範囲の指定)
最も制限の強いアクセス修飾子で、下記の条件を満たす場合のみにアクセスできます。
- 同じファイル内
- 同じ型
class A {
private var privateVar = ""
}
extension A {
func f() {
// ✅OK
print(privateVar)
}
}
class B {
let a = A()
func f() {
// ❗️error: 'privateVar' is inaccessible due to 'private' protection level
a.privateVar
}
}
protocol
プロトコルを宣言します。
public
アクセス修飾子の一種です (クラス、変数、定数、メソッド、関数の公開範囲の指定)
同じターゲット(モジュール)外からアクセス可能になります。open
と異なり、継承は禁止されます。
主に、ライブラリなどで API として公開するものに対して指定します。
関連: private, internal, open
static
static 変数や static function を宣言します。
protocol での宣言時 (下記参照) は class func
と class var
も意味に含みます。
protocol SampleProtocol {
static func staticFuncInProtocol()
static var staticVarInProtocol: Int { get }
}
class Sample {
// `static func staticFuncInProtocol()` can be implemented by `static func` or `class func`
class func staticFuncInProtocol() {}
// ditto
class var staticVarInProtocol: Int { 1 }
}
some
型の前に記述され、呼び出し元に対して具体的な型情報を隠蔽します。(ただし、コンパイラは具体的な型情報を知っている状態となります)
詳細
SwiftUI では下記のように使われています。
var body: some View {
Text("Hello, World!")
}
body
の内部では Text
という concrete type(具体的な型)を返していますが、呼び出し元では Text
型とは分からない状態です。分かるのは、「View に準拠した"とある(some)"型である」ということです。
それならば、some
がなくても同じだと思うかもしれません。
var body: View {
Text("Hello, World!")
}
この場合でも具体的な型情報は隠蔽されています。しかし、この場合はコンパイラにも抽象型として扱われます。
一方、some
をつけた場合、コンパイラには具体的な型情報がわかっています。「concrete type(具象型)であるが、呼び出し元からは具体的に何の型かは分からない」という状態で扱われます。
参考: the swift programming language/Opaque Types
struct
構造体を宣言します。
subscript
クラスや構造体に [] を実装します。
Objective-C の場合についてはクラスに [], {} を実装するに書いてみました。
class SubscriptSample {
var hoge: Any?
subscript(index: Int) -> String {
get {
return "Int もらいました"
}
set {
hoge = newValue
}
}
subscript(index: String) -> String {
get {
return "String もらいました"
}
// setter なくても良いです
}
subscript(index: AnyObject?) -> String {
return "何かもらいました"
}
subscript(x: Int, y: Int) -> String {
return "\(x), \(y)"
}
subscript() -> String {
return "nothing"
}
}
let subscriptSample = SubscriptSample()
var optionalSample: Int? = 1;
subscriptSample[3] // => "Int もらいました"
subscriptSample["a"] // => "String もらいました"
subscriptSample[nil] // => "何かもらいました"
subscriptSample[optionalSample] // => "何かもらいました"
subscriptSample[1, 2] // => "1, 2"
subscriptSample[] // => "nothing"
typealias
型の別名を宣言(*1)、または、associated type に対して型を指定(*2)します。
typealias IntAlias = Int
typealias Point = (Int, Int)
protocol P {
associatedtype T
}
struct S: P {
typealias T = Any
}
参考:
The Swift Programming Language (Language Reference -> Declaration -> Type Alias Declaration)
The Swift Programming Language (Language Reference -> Declaration -> Protocol Associated Type Declaration)
associatedtype
associated type (付属型) の宣言をします。
protocol SampleProtocol {
associatedtype AssociatedType // 付属型を宣言します
func sampleFunc(param :AssociatedType) -> AssociatedType
}
struct SampleStruct: SampleProtocol {
func sampleFunc(param: Int) -> Int { // 付属型が Int であると決定されます
return param + param
}
}
参考:
The Swift Programming Language (Language Reference -> Declaration -> Type Alias Declaration)
The Swift Programming Language (Language Reference -> Declaration -> Protocol Associated Type Declaration)
var
変数を宣言します。
Keywords used in statements
break
switch 文やループから抜けます。
for, while の前にラベルをつけることで抜けるブロックを指定できます。
詳細: [document の Control Flow -> Labeled Statements] (https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html)
var i = 0
firstLoop: while true {
print("first loop: \(i)")
while true {
i += 1
print("second loop: \(i)")
switch i {
case 5:
print("break firstLoop")
break firstLoop
default:
break
}
}
}
print("finish: \(i)")
first loop: 0
second loop: 1
second loop: 2
second loop: 3
second loop: 4
second loop: 5
break firstLoop
finish: 5
関連: continue, fallthrough
case
列挙子リストの宣言、switch 文内で条件分岐、switch 文内以外で if, for との併用により、パターンマッチングができます。
let optionalSample: Int? = 1;
let optionalArraySample: [Int?] = [1, 2, nil, 3]
if case let x? = optionalSample {
print("optionalSample: \(x)")
}
for case let x? in optionalArraySample {
print("optionalArraySample: \(x)")
}
optionalSample: 1
optionalArraySample: 1
optionalArraySample: 2
optionalArraySample: 3
参考:
Swift 2: Pattern Matching with “if case”,
The Swift Programming Language (Patterns -> Optional Pattern)
関連: enum
continue
次のループ処理へ移動します。
break と同様にラベルをつけることで移動するループ処理を指定することができます。
var i = 0
firstLoop: while true {
print("first loop: \(i)")
if i != 0 { break }
while true {
i += 1
print("second loop: \(i)")
switch i {
case 5:
print("continue firstLoop")
continue firstLoop
default:
break
}
}
}
print("finish: \(i)")
first loop: 0
second loop: 1
second loop: 2
second loop: 3
second loop: 4
second loop: 5
continue firstLoop
first loop: 5
finish: 5
関連: break, fallthrough
default
switch 文内の条件分岐で、case に当てはまらなかった場合の処理の宣言をします。
関連: switch, case
defer
スコープを抜ける際に実行する処理を記述します。
func deferSample() {
defer {
print("in defer")
}
print("end of scope")
}
deferSample()
end of scope
in defer
参考:
The Swift Programming Language (Statements -> Defer Statement)
The defer keyword in Swift 2: try/finally done right
do
スコープを作成します。
catchを繋げることで、スコープ内で発生した例外をcatch文で処理することができます。
関連: catch, try
else
条件分岐で使用します。
また、guard文では文法上必須となります。
関連: if, guard
fallthrough
switch 文の中で、マッチした case 文の次の case 文または default 文内の処理を実行します。
(厳密には、fallthrough
の所属している case ブロックの中から抜け、所属する case 文の次の case 文または default 文内の処理を実行します)
let a = 1
switch a {
case 1:
print("1")
fallthrough
case 2:
print("2")
default:
print("default")
}
switch (a) {
case 1:
printf("1\n");
case 2:
printf("2\n");
break;
default:
printf("default\n");
}
1
2
補足
下記の補足です。
厳密には、
fallthrough
の所属している case ブロックの中から抜け、所属する case 文の次の case 文または default 文内の処理を実行します。
下記のように、fallthrough
が呼ばれると、即座に case ブロックから抜けます。
そのため、その下の print("1-2")
は実行されません。
switch 1 {
case 1:
defer { print("defer") } // ブロック(スコープ) から抜ける様子を観察
print("1-1")
if true {
fallthrough
}
print("1-2")
case 2:
print("2")
default:
print("default")
}
1-1
defer
2
for
繰り返し処理を記述できます。
for i in 1...10 {
// 処理
}
for _ in 1...10 {
// 処理
}
guard
変数または定数が条件に一致するか評価し、一致していない場合、続く else のブロックが実行されます。
このブロックの中では必ず以下のいずれかを使用し、guard が記述されているスコープを抜けなくてはいけません。
- return
- break
- continue
- throw
- fallthrough
- fatalError 等の
Never
型を返す関数の呼び出し
func guardSample1(value: Int?) -> String {
guard let value = value, value > 10 else {
// この中では必ずスコープを抜ける処理を書きます
return "in else block"
}
// ここからは value がアンラップされ、また、10 より大きいことが保証されます
return "\(value)"
}
guardSample1(nil) // => in else block
guardSample1(10) // => in else block
guardSample1(100) // => 100
func guardSample2(a: String, b: Int?) -> String {
// 複数の変数・定数の評価もできます
guard let intValue = Int(a), let b = b else {
return "in else block"
}
// ここからは b がアンラップされます
return "\(intValue + b)"
}
guardSample2("a", b: 1) // => in else block
guardSample2("1", b: 1) // => 2
guardSample2("1", b: nil) // => in else block
関連: return, break, continue, throw
if
続く条件を評価し、一致した(true)場合ブロック内を実行します。
unwrap する場合にも使用します。この構文を Optional binding と言います。詳細はこちら ([Swift] Optional 型についてのまとめ Ver2) に解説されていて分かりやすかったです。
func ifLetSample(value: Int?) {
if let a = value {
type(of: value) // Optional<Int>.Type
type(of: a) // Int.Type
}
}
in
文脈によって下記のように意味が変わります。
- クロージャのボディの開始箇所を表します
- for ~ in の形で取り出す要素の配列を指定します
関連: for
repeat
C言語等の言語における
do {...} while(...)
の do
の役割です。
関連: while
return
返り値を指定します。
返り値は下記の 2 パターンです。
- 要素が一つの場合はその要素
- それ以外の場合は tuple
func sample() {
return
}
var a = sample() // -> ()
参考: Remove @noreturn attribute and introduce an empty Never type
switch
条件分岐を行います。
switch (1, "a") {
case (1, "b"):
print("1, b")
case (1, _):
print("1, _") // ここがマッチします。 (_ はワイルドカード)
case (1, "a"):
print("1, a") // 上がマッチするので評価されません
}
関連: case
where
マッチングの条件を追加します。
while
下記の2種類の繰り返し構文を記述できます。
while condition {
statements
}
repeat {
statements
} while condition
関連: repeat
Keywords used in expressions and types
as
大きく分けて、2 種類の役割があります。
- キャスト
class A {}
let anyObj: AnyObject = A()
let a = anyObj as! A // AnyObject から A にキャスト
- 型を明示すること
let v = 1 as Double
any
型が existential type であることを明示します。
any
での明示は Swift5 では必須ではありませんが、Swift6 からは必須です。
(ただし、Any
と AnyObject
に対しては any
は必須ではありません。)
詳細
例えば、protocol P
に準拠した型を P
型に抽象化して(existential type として)扱いたい場合を考えます。let p1: any P = S()
のように型の宣言部に any
をつけることで、ここでの P
が existential type であることを明示できます。
protocol P {}
struct S: P {}
let p1: any P = S()
Any
すべての型のインスタンス (関数型も含む) を表現します。
実態は空の protocol です。
参考:
Type Casting for Any and AnyObject
Import Objective-C id as Swift Any type
catch
例外が投げられた際にブロック内が実行されます。
false
真偽値リテラルの一種で、偽を表します。
is
ある型またはあるプロコトルを実装した型として振る舞えるかどうかを検査します。
1 is Int // -> true
(1, 1) is AnyObject // -> false
(1, 1) is (Int, Int) // -> true
// プロトコルの検査
protocol SampleProtocol { }
class SampleClass: SampleProtocol { }
let sampleClassInstance = SampleClass()
sampleClassInstance is SampleClass // true
sampleClassInstance is SampleProtocol // true
nil
nil リテラルを表します。
Optional.None == nil // -> true
rethrows
引数にとったクロージャが投げた例外を呼び出し元に対して更に投げます。
func sample(callback: () throws -> Int) rethrows {
try callback()
}
super
親クラスを表します。
self
- インスタンスメソッド内などで単独で使用した場合、インスタンス自身を返します
- [expression (式) に対して呼び出した場合、式が評価された値が返ります](#expression (式) に対して呼び出した場合、式が評価された値が返ります )
- [type (型) に対して呼び出した場合、自身の型が返ります](#type (型) に対して呼び出した場合、自身の型が返ります )
インスタンスメソッド内などで単独で使用した場合、インスタンス自身を返します
class Sample {
var a: Int?
func sampleMethod() -> Sample {
a = 1
return self // 自身 (playground 上では Sample と見えますが、プロパティ a が変更されているので上で作成したインスタンスだと確認できます)
}
}
expression (式) に対して呼び出した場合、式がそのまま返ります
<#expression#>.self
(1 + 1).self
の返り値は (1 + 1)
という式と同等です。
(1 + 1).self // (1 + 1)
(1 + 1).self // 2 as Int ではない
// 証明
(1 + 1).self + 1.0 // OK
(1 + 1) + 1.0 // OK
let exp = 1 + 1 // 2 as Int
exp + 1.0 // Error (type mismatch)
type (型) に対して呼び出した場合、自身の型が返ります
<#type#>.self
class Sample {
}
Sample.self // -> Sample.Type
Sample.self.init() // -> Sample のインスタンス (= Sample.self は自身の型を返しています)
参考: The Swift Programming Language (Language Reference -> Expressions -> Postfix Self Expression)
Self
自身の型を返します。
throw
例外を投げます。
throws
メソッド、関数の宣言部に書き、例外が投げられる可能性があることを示します。
true
真偽値リテラルの一種で、真を表します。
try
例外が投げられる可能性のある関数・メソッドを実行します。
Keywords that begin with a number sign (#)
#available
OS 及びそのバージョンによる条件を表現します。
if #available(iOS 10.0, *) {
print("iOS10 or later")
}
関連: #unavailable
#unavailable
[Swift 5.6~]
OS 及びそのバージョンによる条件を表現します。
if #unavailable(iOS 15.0) {
// Old functionality
} else {
// iOS 15 functionality
}
関連: #available
#colorLiteral
下記の構文で色を表現するリテラルを記述できます。
#colorLiteral(red: <# expression #>, green: <# expression #>, blue: <# expression #>, alpha: <# expression #>)
型を明示しない場合は、AppKit モジュールをインポートした場合は NSColor 型、UIKit モジュールをインポートした場合は UIColor 型として解釈されます。
#column
#column が評価された場所の列番号 (Int)
#if, #else, #elseif, #endif
コンパイル時に指定のコードを含めるか否かを制御します。
#if <# Custom Flag に設定した名前 (設定せずにコンパイルすると無視される) #>
print("DEBUG")
#endif
#if swift(>=3.0)
print("Swift3.0")
#endif
#if arch(arm64)
print("arm64")
#endif
#if os(OSX)
print("OSX")
#endif
#if swift(>=3.0)
print(">=Swift3.0")
#elseif swift(>=2.0)
print(">=Swift2.0")
#else
print("else")
#endif
参考:
#error
記載した行に対するエラーを compile 時に発生させます。
関連: #warning
#file
#file が評価された場所のパス(String)を返します。
ただし、今後の Swift のバージョンで動作の変更があるため #fileID か #filePath への置き換えが推奨されています。
Swift5.5時点では #filePath と同じ動作をしますが、今後のバージョンでは #fileID の動作へ変更されます。
// sample project の中の sampleApp.swift で使った場合
print("#file", #file)
print("#filePath", #filePath)
print("#fileID", #fileID)
#file /Users/ezura/workspace/article/swift5/sample/sampleApp.swift
#filePath /Users/ezura/workspace/article/swift5/sample/sampleApp.swift
#fileID sample/sampleApp.swift
参考: the swift programming language > Expressions > Literal Expression
#fileID
#fileID が評価されたファイルとそのモジュール名を module名/file名 の形式で返します(String型)。
// sample project の中の sampleApp.swift で使った場合
print("#fileID", #fileID)
#fileID sample/sampleApp.swift
関連: #filePath, #file
参考: the swift programming language > Expressions > Literal Expression
#filePath
#filePath が評価された場所のパス(String)を返します。
// sample project 内の sampleApp.swift で使った場合
print("#filePath", #filePath)
#filePath /Users/ezura/workspace/article/swift5/sample/sampleApp.swift
関連: #file, #fileID
参考: the swift programming language > Expressions > Literal Expression
#fileLiteral
下記の構文でファイルリテラルを記述できます。
#fileLiteral(resourceName: "<# ファイル名 #>")
型を明示しない場合は URL 型として解釈されます。(Foundation モジュールがインポートされている場合のみ)
また、ファイルが取得できなかった場合、実行時エラーとなります。
参考:
#function
#function が評価された場所の関数・メソッドの名前 (String) を表現します。
#imageLiteral
下記の構文で画像リソースを表現するリテラルを記述できます。
#imageLiteral(resourceName: "<# ファイル名 #>")
型を明示しない場合は、AppKit モジュールをインポートした場合は NSImage 型、UIKit モジュールをインポートした場合は UIImage 型として解釈されます。
#keypath
key や keypath (string literal) を生成します。
class Object: NSObject {
let value = 1
var matryoshka: Object?
}
#keyPath(Object.value) // "value"
#keyPath(Object.matryoshka.value) // "matryoshka.value"
let object = Object()
object.matryoshka = Object()
object.value(forKey: #keyPath(Object.value)) // 1
object.value(forKeyPath: #keyPath(Object.matryoshka.value)) // 1
関連:
#line
#line が評価された場所の行番号 (Int)
#selector
Selector を生成します。
Swift3 から、以前の機能に加えて Objective-C で記述された class のプロパティの getter, setter メソッドの Selector を \#selector
を用いて生成できるようになりました。(以前は \#selector
が対応していなかったため、文字列で指定していました)
@interface ViewController : UIViewController
@property(assign) int num;
@end
#selector(getter: ViewController.num)
#selector(setter: ViewController.num)
参考:
- Using Swift with Cocoa and Objective-C (Swift 3) > Interacting with Objective-C APIs
- Hannibal #selector
#sourceLocation
#line, #file, #filePath, #fileID の値を操作します。
- 値の変更:
#sourceLocation(file: file path, line: line number)
- 変更をリセット:
#sourceLocation()
ただし、Playground 上ではリセット後の line の値が不正になるバグがあります。(Xcode13.1時点で確認)
参考:The Swift Programming Language > Line Control Statement
#warning
記載した行に対する warning を compile 時に発生させます。
関連: #error
#dsohandle
#line, #file, #function と同様に、書かれている場所に関する情報を表すキーワードです。
自身が書かれているライブラリがロードされている場所のアドレスを表します。(アドレスなので、他のキーワードと違い、UnsafePointer
型です)
provides an UnsafePointer to the current dynamic shared object (.dylib or .so file)
引用: Modernizing Swift's Debugging Identifiers
特定の文脈でのみ予約語として使用
assignment
左右に値を取る演算子を宣言した際に、Optional Chaining 評価の一連の流れで演算するかを指定します。
true を指定すると、Optional Chaining 評価の一環として演算を行おうとします。false は既定の動作で、Optional Chaining の評価が終わってから、その評価結果と演算します。
precedencegroup OperationFoldedIntoOptionalChaining {
assignment: true
}
precedencegroup OperationOutsideOfOptionalChaining {
assignment: false
}
infix operator ~~ : OperationFoldedIntoOptionalChaining
infix operator ~^ : OperationOutsideOfOptionalChaining
func ~~ (left: Int, right: Int) -> Int {
print("~~")
return left + right
}
func ~^ (left: Int?, right: Int) -> Int {
print("~^")
return left?.advanced(by: right) ?? -1
}
let values = nil as [Int]?
values?.count ~~ 5 // => nil
values?.count ~^ 5 // => -1
/*
~~ 演算は、
最初に `values` の nil 判定、nil でなければ `.count ~~ 5` を評価
values.map { $0.count ~~ 5 } と同等。この例では `~~` 演算は実行されない。
~^ 演算は、最初に `values?.count` を評価、続いて `その結果 ~^ 5` を評価
普通に `(values?.count) ~^ 5` としたのと同等。演算子の既定の動作。
*/
関連: infix, operator, precedencegroup
associativity
左右に値を取る優先度グループを宣言した際に、結合方向を指定します。
指定できる値は下記の 3 種類です。
precedencegroup <#precedence group name#> {
higherThan: <#lower group name#>
associativity: <#left | right | none#>
}
precedencegroup MyAddition {
associativity: none
}
infix operator +++ : MyAddition
infix operator --- : MyAddition
func +++ (left: Int, right: Int) -> Int {
return left + right
}
func --- (left: Int, right: Int) -> Int {
return left - right
}
1 +++ 1 --- 1
/*
error: adjacent operators are in non-associative precedence group 'MyAddition'
1 +++ 1 --- 1
^ ~~~
*/
関連: left, right, none, operator, precedencegroup
convenience
init の前に記述することで、convenience initializer を宣言します。
関連: init
dynamic
Objective-C のランタイムを使用して値にアクセスします。
dynamic <#var | let #> <# name #>
詳細
Objective-C runtime と pure Swift では、呼び出す処理を決定する方法 (method dispatch) が異なります。
Objective-C runtime の場合、実行時に Class に対して問い合わせて実行すべき処理を探していきます (message passing 方式)。
例えば、instance.methodA()
を実行するとき、instance
の持つ Class の情報に対して、"methodA" (呼び出したい function 名) があるか検索します。無かったらその Class の親 Class に対して問い合わせる、なかったら更にその親 Class に、といった処理を実行時に行います。
一方、Swift ではコンパイル時に「型に対してどんな method があるか」という情報を持つ table を作ります。直接呼び出す場合もありますが、継承関係がある場合など、実行時にしか呼び出す処理が決まらない場合はこの table を参照して処理を決定します。重要なのは、この table はコンパイル時に作られてから後で変更できないということです。
Objective-C の場合、Class の情報を書き換えることができます。つまり、実行時に method を増やしたり、method 名を変えたり、処理を入れ替えたりできます。(method swizzling と呼ばれる操作です)
このような Objective-C の機能を使いたい場合、dynamic
を指定することで、Swift の method table 上には載らず、Objective-C の機構を使って処理するようになります。
didSet
Stored プロパティまたは変数の値が変更された際の処理を宣言します。
final
継承、オーバーライドを不可にします。
get
文脈によって意味が異なります。
computed property 内: プロパティにアクセスした際の処理
class A {
var value: Int {
get { return 1 }
}
}
protocol 内で宣言した場合: 値が取得可能であることの宣言
protocol Sample {
var value: Int { get }
}
class A: Sample {
let value = 1
}
class B: Sample {
var value: Int { return 1 }
}
infix
左右に被演算子をとる演算子の処理を定義します。
infix operator ☁
func ☁ (left: Int, right: Int) -> Int {
return left + right
}
1 ☁ 2 // => 3
関連: operator
indirect
列挙体を列挙子の中で再帰的に使えるようになります。つまり、Json のような入子構造を表現できます。
enum SampleEnum {
case num(Int)
indirect case indirectNum(SampleEnum)
}
または
indirect enum SampleEnum {
case num(Int)
case IndirectNum(SampleEnum)
}
// 入子にできる
SampleEnum.indirectNum(
.indirectNum(
.indirectNum(
.num(1)
)
)
)
詳細:
indirect を指定すると associated value を間接指定するようになります。
間接指定しない場合、associated value 分のメモリサイズが確定できないため、その列挙体のために確保するメモリサイズも決まりません。
間接指定する場合、associated value の場所(アドレス)を保持することになるので列挙体のサイズが確定できるようになります。
詳細: Swift Programming Language (Enumerations -> Recursive Enumerations)
lazy
変数を初期化する際の値を遅延評価します。
通常 property は instance の作成時に値が評価されますが、下記のように lazy を指定すると該当の property に初めてアクセスした際に値が評価されます。
class Sample {
lazy var lazyValue = Date()
let defaultValue = Date()
}
let sample = Sample()
sleep(10)
// lazyValue 2021-11-18 05:14:47 +0000
print("lazyValue", sample.lazyValue)
// defaultValue 2021-11-18 05:14:37 +0000
print("defaultValue", sample.defaultValue)
使い所としては、
- 該当のpropertyに入る値の生成にコストがかかるため、実際に使うときまでその生成を遅延させたい場合
- init 時に値が決まらないため、後で値を代入したい場合
があります。
2 番目に関しては少々トリッキーなので好みが分かれますが、下記のように IUO を避ける実装ができます。
class Sample {
var iuoValue: String! // init 時に値が決まらないため IUO にしている
// ↑のようなIUO を使うのを避ける実装。最初は`Never`(全ての型のサブタイプ扱い)を返すclosure
を入れておき、このpropertyにアクセスする前には値を入れる前提。
lazy var lazyEval: String = { preconditionFailure() }()
}
left
演算子を定義した際に、左結合を指定します。
詳細: このページの associativity 項
関連: associativity, operator, right, none
mutating
値型のオブジェクトにおいて、自身または自身のプロパティを書き換えるインスタンスメソッドに対して宣言する。
enum SampleEnum {
case A, B, C
case a, b, c
mutating func upperCase() {
switch self {
case .a: self = .A
case .b: self = .B
case .c: self = .C
default: break
}
}
}
struct SampleStruct {
var x = 0
mutating func modifyX(x: Int) {
self.x = x
}
mutating func reset() {
self = SampleStruct()
}
}
none
演算子を定義した際に、結合方向を指定しません。
詳細: このページの associativity 項
関連: associativity, operator, right, left
nonisolated
[Swift 5.5~]
Actorの宣言部で使用します。
Actor の基本思想は「ミュータブルなデータ(状態)を actor 外から隔離(isolate)しデータの同期状態を守る」ことです。そのため、actor の method や mutable な property はデフォルトで隔離(isolate)対象となっています。
その対象としないことを指定する場合に nonisolated
を付与します。
actor SampleActor {
nonisolated func nonisolatedFunc() {}
nonisolated var nonisolatedComputedProperty: String {
"computedProperty getter"
}
}
ミュータブルなデータ(状態)の保護状態を守るため、隔離対象外(nonisolated
)の method/computed property は隔離対象の property や method へのアクセスが禁止されます。これに違反するとコンパイルエラーになります。
関連: actor
nonmutating
値型のインスタンスメソッドが自身に変更を加えないことを宣言します。
使い所:
computed property 内で定義する set はデフォルトで mutating 指定になります。iOS の API 内では下記のように setter の mutaing を無効にするために使用しています。
var value: Value { get nonmutating set }
optional
プロトコルで指定されたメソッドやプロパティの実装を任意とします。
override
親クラスのメソッドやプロパティをオーバーライドする際に宣言します。
postfix
独自の後置演算子を定義します。
postfix operator ***
postfix func *** (a: inout Int) -> Int {
a *= a
return a
}
var hoge = 4
hoge*** // => 16
prefix
独自の前置演算子を定義します。
prefix operator ***
prefix func *** (a: inout Int) -> Int {
a *= a
return a
}
var hoge = 4
***hoge // => 16
Protocol
Protocol のメタタイプを取得します。
let protocolMetatype: SampleProtocol.Protocol = SampleProtocol.self
関連: Type
required
サブクラスにイニシャライザのオーバーライドを強制します。
また、サブクラスでそのイニシャライザをオーバーライドする際には override ではなく、required を指定します。
right
演算子を定義した際に、右結合を指定します。
詳細: このページの associativity 項
関連: associativity, operator, left, none
set
文脈によって意味が異なります。
computed property 内: プロパティにアクセスした際の処理
class A {
var value: Int {
get { return 1 }
set {}
}
}
protocol 内で宣言した場合: 値を受け渡し可能であることを宣言
protocol Sample {
var value: Int { get set }
}
class A: Sample {
var value = 1
}
class B: Sample {
var value: Int {
get { return 1 }
set {}
}
}
Type
クラス、構造体、列挙体のメタタイプを取得します。
class Sample {
required init() {}
}
let metatype: Sample.Type = Sample.self
let instance = metatype.init()
関連: Protocol
unowned
弱参照の変数を宣言します。
weak と違い、参照している値よりも生存期間が短い、つまり、アクセスした際に参照先が解放されていない (nil となっていない) ことを前提とします。値が破棄されているとランタイムエラーになります。
// unowned = unowned(safe)
unowned var safe: AnyObject
// capture list 内で指定することが多いです
{ [unowned a] in /* ... */ }
関連: weak, unowned(safe), unowned(unsafe)
unowned(safe)
unowned
へ修飾することで参照の動作を指定できます。
unowned
(何も指定しなかった場合)は unowned(safe)
扱いとなります。関連する指定に unowned(unsafe)
があります。
// unowned = unowned(safe)
unowned(safe) var safe: AnyObject
// capture list 内で指定することが多いです
{ [unowned(safe) a] in /* ... */ }
関連: unowned(unsafe), unowned
unowned(unsafe)
unowned
へ修飾することで参照の動作を指定できます。
unowned
または unowned(safe)
と違い、参照先が解放された際にアクセスしても nil
と評価されません。つまり、解放されている場所を指し続けているためメモリに対して安全なアクセスをしません。
Objective-C の __unsafe_unretained
と同様の動作です。
// like `__unsafe_unretained`
unowned(unsafe) var unsafe: AnyObject
{ [unowned(unsafe) a] in /* ... */ }
関連: unowned(safe), unowned
weak
弱参照の変数を宣言します。
unowned と違い、参照している値が nil になることを許容します。
willSet
stored property または変数への代入が実行される前の処理を記述します。
関連: didSet
その他
_
ワイルドカード
switch (1, "a") {
case (1, "b"):
print("1, b")
case (1, _):
print("1, _") // ここがマッチします。 (_ はワイルドカード)
}
関連: case
引数名の省略
class Sample {
var a: Int
init(param: Int) {
a = param
}
}
let sample = Sample(param: 1)
class Sample {
var a: Int
init(_ param: Int) {
a = param
}
}
let sample = Sample(1)
値を捨てる
let a: Int
(a, _) = (0, 1)
a // -> 0
型の補完(Type placeholder)
Swift5.6〜
型を宣言する部分に対して _
を記述するとその部分が型推論される。
つまり、型の明示を省略できる。
let dict: [_: String] = [0: "zero", 1: "one", 2: "two"]
// -> [Int: String]
let count: _? = 0
// -> Int?