構造体とクラス

他のプログラミング言語とは異なり、Swiftでは、カスタム構造体とクラス用にインターフェースとインプリメンテーションのための個別のファイルを作成する必要はありません。Swiftでは、一つのファイルで構造体またはクラスを定義すると、そのクラスまたは構造体への外部インターフェイスが他のコードで自動的に使用できるようになります。

注意

クラスのインスタンスは、伝統的にオブジェクトとして知られています。しかし、Swiftの構造体とクラスは、他の言語に比べてはるかに近い機能を持ちます。この章では、構造体とクラスのインスタンスに適用でする機能について説明します。以下では、インスタンスは、一般的用語として使用します。

構造体とクラスの比較

Swiftの構造体とクラスには次の多くの共通点があります。

  • 値を格納するプロパティを定義する
  • 機能を提供するメソッドを定義する
  • サブスクリプト文法を使用して値へのアクセスを提供するサブスクリプトを定義する
  • 初期化を定義して初期状態を設定する
  • デフォルトの実装を超えて機能を拡張するために拡張される
  • プロトコルに準拠し、特定の種類の標準機能を提供する

クラスには、構造体にはない追加機能があります。

  • 継承により、あるクラスが別のクラスの特性を継承できるようになります。
  • 型キャストを使用すると、実行時にクラスインスタンスの型を確認およびインタープリットできます。
  • デイニシャライザを使用すると、クラスのインスタンスが、割り当てたリソースを解放できます。
  • 参照カウントは、クラスインスタンスへの複数の参照を可能にします。

クラスがサポートする追加機能には、複雑さが増すという代償があります。一般的なガイドラインとして、推論しやすい構造体を優先し、必要に応じてクラスを使用します。実際、あなたが定義するカスタムデータ型のほとんどは、構造体および列挙型になるでしょう。

定義構文

struct SomeStructure {
    // 構造体定義
}

class SomeClass {
    // クラス定義
}

// 例
struct Resolution {
    var width = 0     // プロパティ
    var height = 0     // プロパティ
}

class VideoMode {
    var resolution = Resolution()      // プロパティ
    var interlaced = false     // プロパティ
    var frameRate = 0.0     // プロパティ
    var name: String?     // プロパティ
}

注意

構造体またはクラス名は、UpperCamelCase を使用します。プロパティ名とメソッド名は、lowerCamelCaseを使用して、型名と区別します。

構造体とクラスのインスタンス

Resolution構造体の定義とVideoModeクラス定義は、形だけを定義します。それら自体は、特定の解像度またはビデオモードを記述していません。そのためには、構造体またはクラスのインスタンスを作成する必要があります。

// インスタンスの作成
let someResolution = Resolution()
let someVideoMode = VideoMode()

プロパティへのアクセス

ドット構文を使用してインスタンスのプロパティにアクセスできます

// プロパティへのアクセス例
print(someResolution.width)

// サブプロパティへのアクセス例
someVideoMode.resolution.width = 1280
print(someVideoMode.resolution.width)

構造型のメンバーのイニシャライザー

すべての構造体は、自動的に生成されるメンバーのイニシャライザーができます。

let vga = Resolution(width: 640, height: 480)

構造体とは異なり、クラスインスタンスはデフォルトのメンバーのイニシャライザーはありません。

構造体と enum は値型

Swiftのすべての基本型(整数、浮動小数点数、ブール値、文字列、配列、および辞書)は値型であり、構造体として実装されています。

すべての構造体と enum は、値型です。つまり、構造体と enum のインスタンス、およびプロパティは、コード内で渡されるときに常にコピーされます。

注意

配列、辞書、文字列などの標準ライブラリによって定義されたコレクションは、値型ですが、コピーのコストを最適化されており、すぐにコピーを作成せずに、元のインスタンスとコピーの間で要素のメモリを共有します。コレクションのコピーの1つが変更された場合、要素は変更の直前にコピーされます。このため、コピーがすぐに行われたかのように見えます。

let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048

この時、プロパティはコピーされます。../_images/sharedStateStruct_2x.png

クラスは参照型です

値型とは異なり、参照型は、変数または定数に割り当てられたとき、または関数に渡されたときにコピーされません。コピーではなく、同じ既存のインスタンスへの参照が使用されます。

let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

クラスは参照型であり、この場合、下の図に示すように、同じ単一インスタンスの2つの異なる名前にすぎません。../_images/sharedStateClass_2x.png

この例は、参照型が推論するのがいかに難しいかについても示しています。プログラムのコードに遠く離れていた場合、ビデオモードが変更されていることを見つけることは困難である可能性があります。値の型の方が簡単に推論できます。

アイデンティティーオペレーター

2つの定数または変数がクラスのまったく同じインスタンスを参照しているかどうかを確認する 2つの アイデンティティオペレーターが用意されています。

  • === 同じ
  • !== 同一ではない
if tenEighty === alsoTenEighty {
    print("tenEightyとalsoTenEightyは、同じインスタンス")
}

独自のカスタム構造体とクラスを定義する場合、2つのインスタンスが等しいと見なされるものを決定するのはユーザーの責任です。==and !=演算子の独自の実装を定義するプロセスについては、「等価演算子」で説明しています。

ポインタ

C、C ++、またはObjective-Cの経験がある場合は、これらの言語がポインターを使用してメモリ内のアドレスを参照していることを知っているかもしれません。ある参照型のインスタンスを参照するSwift定数または変数は、Cのポインターに似ていますが、メモリ内のアドレスへの直接のポインターではなく、アスタリスク(*)を記述する必要はありません。代わりに、これらの参照はSwiftの他の定数または変数と同様に定義されます。標準ライブラリは、ポインタと直接対話する必要がある場合に使用できるポインタとバッファタイプを提供します。「手動メモリ管理」を参照してください。

Enumeration

構文

enum SomeEnumeration {
    // enumeration definition goes here
}

// 例
enum CompassPoint {
    case north
    case south
    case east
    case west
}

// 注意
// Cなどと異なり、デフォルトで整数値は設定されない。代わりに、明示的に定義されたCompassPoint型

// カンマで区切って、複数のケースを1行に表示できます。
enum Planet {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}

// enum の名前は大文字で始め、単数形で表記すること

var directionToHead = CompassPoint.west
// directionToHeadの型は、CompassPoint型として推測できる
// したがって、値を設定するときに型を省略できます。
directionToHead = .east

Switchステートメントを使用した列挙値のマッチング

directionToHead = .south
switch directionToHead {
case .north:
    print("Lots of planets have a north")
case .south:
    print("Watch out for penguins")
case .east:
    print("Where the sun rises")
case .west:
    print("Where the skies are blue")
}
// Prints "Watch out for penguins"

enum caseの列挙

// CaseIterable 型を指定することにより、allCases で全てのケースを列挙できる
enum Beverage: CaseIterable {
    case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available")
// Prints "3 beverages available"

for beverage in Beverage.allCases {
    print(beverage)
}
// coffee
// tea
// juice

関連する値

UPCバーコードは4つの整数のタプル、QRコードバーコードを任意の長さの文字列として保存すると便利です。

// UPCバーコードとQRコードバーコードの両方を次のように表すことができる
enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}

var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")

switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
    print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
    print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."

Raw Value

// Raw Value の設定方法
enum ASCIIControlCharacter: Character {
    case tab = "\t"
    case lineFeed = "\n"
    case carriageReturn = "\r"
}

Raw Valueは、文字列、文字、または整数または浮動小数点型のいずれかで、enum 宣言内で一意である必要があります。

暗黙的に割り当てられた Raw Value

整数または文字列の未加工値を格納する列挙型を使用している場合、各ケースに未加工値を明示的に割り当てないと、自動的に値を割り当てます。

たとえば、生の値に整数が使用されている場合、各ケースの暗黙的な値は前のケースよりも1つ多くなります。最初のケースに値が設定されていない場合、その値は0です。

以下の列挙はPlanet、太陽からの各惑星の順序を表す整数の生の値を使用して、以前の列挙を改良したものです。

enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
// この場合、Planet.venusは2が割り当てられます。

enum CompassPoint: String {
    case north, south, east, west
}
// この場合、CompassPoint.south は "south"が割り当てられます。

let earthsOrder = Planet.earth.rawValue
// earthsOrder is 3

let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is "west"

Raw Value からの初期化

let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.uranus
// すべての可能な値が一致する惑星を見つけるわけではありません。このため、Raw Value の初期化子は常にオプショナルの Enum型のケースを返します。

// 例えば、Raw Value が 11 の惑星は nil が返る
let positionToFind = 11
if let somePlanet = Planet(rawValue: positionToFind) {
    switch somePlanet {
    case .earth:
        print("Mostly harmless")
    default:
        print("Not a safe place for humans")
    }
} else {
    print("There isn't a planet at position \(positionToFind)")
}
// Prints "There isn't a planet at position 11"

再帰的 enum

indirectにより、再帰的な enum を表すことができる。

// 単純な算術式を表す再帰的 enum
enum ArithmeticExpression {
    case number(Int)
    indirect case addition(ArithmeticExpression, ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}

// すべてのケースに対して indirect enum を宣言することもできる
indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}

// (5 + 4) * 2 を再帰的 enum で表した例
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))

// 評価する関数の例
func evaluate(_ expression: ArithmeticExpression) -> Int {
    switch expression {
    case let .number(value):
        return value
    case let .addition(left, right):
        return evaluate(left) + evaluate(right)
    case let .multiplication(left, right):
        return evaluate(left) * evaluate(right)
    }
}

print(evaluate(product))
// Prints "18"

この関数は、関連付けられた値を返すだけで、単純な数値を評価します。左側の式を評価し、右側の式を評価してから、それらを加算または乗算することにより、加算または乗算を評価します。

クロージャー

クロージャーの例

        // 例1
        // クロージャー1
        let add = {(val1: Int, val2: Int) -> Int in
            return val1 + val2
        }
        
        // クロージャー2
        let sub = {(val1: Int, val2: Int) -> Int in
            return val1 - val2
        }
        
        func exec(closure: (Int, Int) -> Int) {
            print(closure(3, 2))
        }

        // クロージャーを渡す
        exec( closure: add )
        exec( closure: sub )

        // 例2
        // クロージャー3
        let percentString = {(val: Double) -> String in
            return String(val * 100) + "%"
        }
        
        // label.text = String(divide(16,4))
        print(percentString(0.5))
        
        // 例3
        let completionClosure = {(val: Double) -> String in
            return String(val) + " Sec passed."
        }
        
        func bigTask(completion: (Double) -> (String)) {
            sleep(3)
            let endMessage = completion(3)
            print(endMessage)
        }
        // bigTask 実行
        bigTask(completion: completionClosure)

クロージャーの例2

        var closure1 = { (num1: Int, num2: Int) -> Int in return num1 + num2 }

        // 型推論による省略
        var closure2 = { (num1: Int, num2: Int) in return num1 + num2 }
        
        // 単文の場合は return が省略できる
        var closure3 = { (num1: Int, num2: Int) in num1 + num2 }

        // さらに型推論による省略
        var closure4 = { 1000 + 4 }
        
        // クロージャーをインライン実行
        var x = {
            1000 + 5
        }()
この例だと、まだ意味が推測できるが、次のようなコードを書かれると、パッと見た目に意味がわからず、読みづらい。
    /// - Tag: MLModelSetup
    lazy var classificationRequest: VNCoreMLRequest = {
        do {
            let config = MLModelConfiguration()
            config.computeUnits = .all
            //          let model = try VNCoreMLModel(for: CatDog100(configuration: config).model)
            let model1 = try SeaOfCloudClassifier1(configuration: config).model
            let model = try VNCoreMLModel(for: model1)
            
            let request = VNCoreMLRequest(model: model, completionHandler: { [weak self] request, error in
                self?.processClassifications(for: request as! VNCoreMLRequest, error: error)
            })
            request.imageCropAndScaleOption = .centerCrop
            return request
        } catch {
            fatalError("Failed to load Vision ML model: \(error)")
        }
    }()

以下は、Swift Programming Language 5.2 より。

クロージャーは、コード内で受け渡し、使用できる機能の自己完結型ブロックです。Swiftのクロージャーは、CおよびObjective-Cのブロックや、他のプログラミング言語のラムダに似ています。

クロージャーは次の3つの形式のいずれかを取ります。

  • グローバル関数は、名前があり、値をキャプチャしないクロージャです。
  • ネストされた関数は、名前を持つクロージャーであり、内なる関数から値を取り込むことができます。
  • クロージャ式は、軽量の構文で記述された名前のないクロージャであり、それを取り囲むコンテキストから値をキャプチャできます

クロージャ式の構文

クロージャ式の構文は、次の一般的な形式を持っています。inキーワードによってクロージャーの本体が始まります。

{ (parameters) -> return type in
    statements
}

次は、sorted(by:)メソッドをクロージャー式で簡潔に表現した例に説明します。

// sorted メソッド
// Swiftの標準ライブラリは、sorted(by:)メソッドを提供します。
       let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
        
// 2要素の大小比較をする関数表記
func backward(_ s1: String, _ s2: String) -> Bool {
   return s1 > s2
}
let r1 = names.sorted(by: backward)
print(r1)
        
// これをクロージャー式で表すと次のように書ける
let r2 = names.sorted(by: { (s1: String, s2: String) -> Bool in
    return s1 > s2
})
print(r2)
        
// さらに型を推論できるので、型を省略する
let r3 = names.sorted(by: { s1, s2 in return s1 > s2 } )
print(r3)

// $0, $1 を使い、引数名を省略できる
let r4 = names.sorted(by: {$0 > $1 })
print(r4)
        
// さらに、Operator Method により、ここまで簡素化できる
let r5 = names.sorted(by: > )
print(r5)

Trailing クロージャー

// 次のクロージャーを Trailing クロージャーへ変換する
let r4 = names.sorted(by: {$0 > $1 })
print(r4)

// クロージャ式が関数またはメソッドの唯一の引数の場合、括弧 () を省略できます。

 // Trailing クロージャー
let r6 = names.sorted{$0 > $1 }
print(r6)

値のキャプチャ

クロージャは、それが定義されている周囲のコンテキストから定数と変数をキャプチャできます。その後、定数と変数を定義した元のスコープが存在しなくても、クロージャーはその本体内からこれらの定数と変数の値を参照および変更できます。

Swiftでは、値をキャプチャできる最も単純なクロージャの形式は、別の関数の本体内に記述されたネストされた関数です。ネストされた関数は、その外部関数の引数をキャプチャでき、外部関数内で定義されている定数と変数もキャプチャできます。

これは、makeIncrementerという関数の例です。これには、incrementerというネストされた関数が含まれています。ネストされたincrementer()関数は、周囲のコンテキストから2つの値runningTotalとamountを取得します。これらの値を捕捉した後、クロージャーincrementerによってrunningTotalが返される。

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}

let incrementByTen = makeIncrementer(forIncrement: 10)

この例では、呼び出されるたびに変数に10追加されるインクリメンター関数を参照するために呼び出される定数を設定します。

incrementByTen()
// 10
incrementByTen()
// 20
incrementByTen()
// 30

let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// 7

incrementByTen を再度呼び出すと、その runningTotal変数がインクリメントされるが、incrementBySevenによりキャプチャされた変数には影響しません

incrementByTen()
// 40

注意

クラスインスタンスのプロパティにクロージャーを割り当て、クロージャーがインスタンスまたはそのメンバーを参照することによってそのインスタンスをキャプチャする場合、クロージャーとインスタンスの間に強い参照サイクルが作成されます。Swiftはキャプチャリストを使用して、これらの強力な参照サイクルを壊します

クロージャは参照型です

上記の例ではincrementBySevenincrementByTenは定数ですが、これらの定数が参照するクロージャrunningTotalは、キャプチャした変数をインクリメントできます。これは、関数とクロージャが参照型であるためです。

let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// returns a value of 50
incrementByTen()
// returns a value of 60

クロージャーのエスケープ

関数の外部で定義されている変数にクロージャーを格納する場合、エスケープが必要になります。

非同期操作を開始する多くの関数は、完了ハンドラーとしてクロージャー引数を取ります。関数は、操作の開始後に戻りますが、操作が完了するまでクロージャーは呼び出されません。この場合、関数の外側でクロージャが格納されるため、クロージャーはエスケープする必要があります。

// 実行部
        let instance = SomeClass()
        instance.doSomething()
        print(instance.x)
        // Prints "200"

        completionHandlers.first?()
        print(instance.x)
        // Prints "100"

var completionHandlers: [() -> Void] = []

class SomeClass {
    var x = 10
    func doSomething() {
        // エスケープクロージャーは、明示的にselfを参照する必要があります。
        escapingClosure { self.x = 100 }
        // ノンエスケープクロージャーは、暗黙的にselfに参照できます。
        nonescapingClosure { x = 200 }
    }
    
    // クロージャーの外側の completionHandlers に追加する場合
    // エスケープしないとコンパイル時エラーになる
    func escapingClosure(completionHandler: @escaping () -> Void) {
        completionHandlers.append(completionHandler)
    }

    func nonescapingClosure(closure: () -> Void) {
        closure()
    }
}

オートクロージャー

autoclosureは、明示的なクロージャーの代わりに通常の式を記述することで、関数のパラメーターの中括弧を省略できます。

オートクロージャーを使用している例

assert(condition:message:file:line:)関数はそのconditionと messageパラメータのオートクロージャーを取得します。そのconditionパラメーターは、デバッグビルドで評価され、そのmessageパラメータがconditionがfalseの場合にのみ評価されています。

クロージャーは呼び出すまで実行されないため、自動クロージャーを使用すると、評価を遅らせることができます。評価の遅延は、コードが評価されるタイミングを制御できるため、副作用があるか計算コストがかかるコードに役立ちます。

// autoclosure ではない場合
func serve1(customer customerProvider: () -> String) {
    print("Now serving \(customerProvider())!")
}

// @autoclosure で宣言する場合
func serve2(customer customerProvider: @autoclosure () -> String) {
    print("Now serving \(customerProvider())!")
}
var customers = ["1st", "2nd", "3rd", "4th"]

// autoclosure ではない場合、呼び出しには中括弧が必要
serve1(customer: { customers.remove(at: 0) } )
// Prints "Now serving 1st"

// @autoclosure で宣言する場合、中括弧を省略できる
serve2(customer: customers.remove(at: 0))
// Prints "Now serving 2nd"

注意

自動クロージャを使いすぎると、コードが理解しにくくなる可能性があります。コンテキストと関数名は、評価が延期されていることを明確にする必要があります。

エスケープを許可するオートクロージャーが必要な場合は@autoclosure属性と@escaping属性の両方を使用します。

// customersInLine is ["Barry", "Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
    customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))

print("Collected \(customerProviders.count) closures.")
// Prints "Collected 2 closures."
for customerProvider in customerProviders {
    print("Now serving \(customerProvider())!")
}
// Prints "Now serving Barry!"
// Prints "Now serving Daniella!"

上記のコードでは、customerProvider引数として渡されたクロージャーを呼び出す代わりに、collectCustomerProviders(_:)関数はクロージャーをcustomerProviders配列に追加します。配列は関数のスコープ外で宣言されています。つまり、関数が戻った後で、配列内のクロージャーを実行できます。その結果、customerProvider引数の値は、関数のスコープをエスケープできるようにする必要があります。

関数

関数の定義と呼び出し

func greet(person: String) -> String {
    let greeting = "Hello, " + person + "!"
    return greeting
}

関数パラメーターと戻り値

// パラメータなしの関数
func sayHelloWorld() -> String {
    return "hello, world"
}

// 複数のパラメーターを持つ関数
func greet(person: String, alreadyGreeted: Bool) -> String {
  if alreadyGreeted {
    return greetAgain(person: person)
  } else {
    return greet(person: person)
  }
}

// 戻り値のない関数
func greet(person: String) {
  print("Hello, \(person)!")
}

注意

厳密に言えば、戻り値が定義されていなくても、関数値を返します。戻り値の型が定義されていない関数は、typeの特別な値 void を返します。これは単に空のタプル()です。

複数の戻り値を持つ関数

関数の戻り値の型としてタプル型を使用して、複数の値を返すことができます。

func minMax(array: [Int]) -> (min: Int, max: Int) {
  var currentMin = array[0]
  var currentMax = array[0]
  for value in array[1..<array.count] {
    if value < currentMin {
      currentMin = value
    } else if value > currentMax {
    currentMax = value
    }
  }
  return (currentMin, currentMax)
}

let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)")

オプショナルタプルの戻り値

func minMax(array: [Int]) -> (min: Int, max: Int)? {
    if array.isEmpty { return nil }
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

注意

タプル全体がオプショナルの(Int, Int)?と、個々のオプショナルの(Int?, Int?)とは意味が異なる。

暗黙的な戻り値を持つ関数

関数の本体全体が単一の式である場合、関数は暗黙的にその式を返します。たとえば、以下の両方の関数は同じ動作をします。

func greeting(for person: String) -> String {
    // return がない、暗黙的な戻り値
    "Hello, " + person + "!"
}
print(greeting(for: "Dave"))

func anotherGreeting(for person: String) -> String {
    return "Hello, " + person + "!"
}
print(anotherGreeting(for: "Dave"))

関数の引数ラベルとパラメーター名

各関数パラメーターには、引数ラベルパラメーター名の両方があります

// 関数の実装で使用されるとき、パラメータ名
func someFunction(firstParameterName: Int, secondParameterName: Int) {
}

// 関数を呼び出すとき、引数ラベル
someFunction(firstParameterName: 1, secondParameterName: 2)

// 引数ラベルを使用すると、読みやすく意図が明確な関数本体を提供しながら、表現力豊かな文のように関数を呼び出すことができます。

// 引数ラベルの省略
// パラメータの引数ラベルが不要_な場合は、そのパラメータの明示的な引数ラベルの代わりにアンダースコア()を記述します。
func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
}
someFunction(1, secondParameterName: 2)
// パラメータに引数ラベルがある場合、関数を呼び出すときに引数にラベルを付ける必要があります。

// デフォルトのパラメータ値
// パラメータのタイプの後にパラメータに値を割り当てることにより、関数内の任意のパラメータのデフォルト値を定義できます。デフォルト値が定義されている場合は、関数を呼び出すときにそのパラメーターを省略できます。
func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) 
someFunction(parameterWithoutDefault: 4) // parameterWithDefault is 12
// デフォルト値を持たないパラメーターを最初に配置します。通常、デフォルト値を持たないパラメータは、デフォルト値を持たないパラメーターより重要です。

// 可変パラメータ
// ...により、可変パラメーターを記述します。
// 関数には、可変引数は1つしか含めることができません。
func arithmeticMean(_ numbers: Double...) -> Double {
    var total: Double = 0
    for number in numbers {
        total += number
    }
    return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers

// 入出力パラメーター
// 関数パラメーターは、デフォルトでは定数です。関数の本体内から関数パラメーターの値を変更しようとすると、コンパイル時エラーが発生します。つまり、誤ってパラメーターの値を変更することはできません。
// パラメータのタイプの直前に inout キーワードを配置することで、入出力パラメータを記述します。入出力パラメーターは、関数によって値が変更され、関数から戻されます。
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

// アンパサンド(&)を変数として引数に入力パラメータとして渡す場合は、関数で変更できることを示します。
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3"

// inout 入出力パラメータにデフォルト値を設定することはできません。


// swapTwoInts(_:_:)関数は、単にaとbの値をスワップします。
 "_" により、関数呼び出し時の引数ラベルが省略可能であることを示しています。
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}
// 
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)

関数のタイプ

すべての関数には、パラメーターの型と関数の戻りの型で構成される特定の関数型があります。

例えば:

func addTwoInts(_ a: Int, _ b: Int) -> Int {
    return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
    return a * b
}
// この関数のタイプは (Int, Int) -> Int です。

func printHelloWorld() {
    print("hello, world")
}
// この関数のタイプは、() -> Void です。

関数タイプの使用

関数型は、Swiftの他の型と同じように使用します。たとえば、定数または変数を関数タイプとして定義し、適切な関数をその変数に割り当てることができます。

var mathFunction: (Int, Int) -> Int = addTwoInts

// 割り当てられた関数を次の名前で呼び出すことができます
print("Result: \(mathFunction(2, 3))")
// Prints "Result: 5"

// 一致する型が同じである別の関数を、同じ変数に割り当てることができます。
mathFunction = multiplyTwoInts
print("Result: \(mathFunction(2, 3))")
// Prints "Result: 6"

パラメータ型としての関数型

(Int, Int) -> Int などの関数型を、別の関数のパラメーターとして使用できます。

func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
    print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// Prints "Result: 8"

戻り型としての関数型

関数の型を別の関数の戻り値の型として使用できます。これを行うに->は、戻り関数の戻り矢印の直後に完全な関数タイプを記述します。

func stepForward(_ input: Int) -> Int {
    return input + 1
}
func stepBackward(_ input: Int) -> Int {
    return input - 1
}

func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    return backward ? stepBackward : stepForward
}

var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)

print("Counting to zero:")
// Counting to zero:
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// 3...
// 2...
// 1...
// zero!

ネストされた関数

この章でこれまでに出てきたすべての関数は、グローバルスコープで定義されたグローバル関数の例です。ネストされた関数と呼ばれる他の関数の本体内に関数を定義することもできます

ネストされた関数は、デフォルトでは外部から隠されていますが、それを囲んでいる関数から呼び出して使用することができます。囲んでいる関数は、ネストされた関数の1つを返すことにより、ネストされた関数を別のスコープで使用することもできます。

func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    func stepForward(input: Int) -> Int { return input + 1 }
    func stepBackward(input: Int) -> Int { return input - 1 }
    return backward ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!

制御フロー

For-Inループ

for– inループを使用して、配列の項目、数値の範囲、文字列の文字などのシーケンスを反復処理します。

// for-in 配列
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
    print("Hello, \(name)!")
}

// for-in 辞書 
// 取得される順序は保証されません
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
    print("\(animalName)s have \(legCount) legs")
}

// for- in数値範囲
for index in 1...5 {
    print("\(index) times 5 is \(index * 5)")
}
// 上記の例でindexは、は定数であり、その値はループの各反復の開始時に自動的に設定されます。そのため、index使用前に宣言する必要はありません。let宣言キーワードを必要とせずに、ループ宣言に含めるだけで暗黙的に宣言されます。

// シーケンスの各値が必要ない場合、変数名の代わりにアンダースコアを使用して値を無視できます。
for _ in 1...5 {
    print("Hello")
}

// 半開範囲演算子で for-in
let minutes = 60
for tickMark in 0..<minutes {
    // render the tick mark each minute (60 times)
}

// for-in-stride(from:, to:, by) 
let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
    // render the tick mark every 5 minutes (0, 5, 10, 15 ... 45, 50, 55)
}

// for-in-stride(from:, through:, by) 閉じた範囲
let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
    // render the tick mark every 3 hours (3, 6, 9, 12)
}

ループ

Swiftには2種類のwhileループがあります。

  • while ループの各パスの開始時にその状態を評価します。
  • repeat– whileループを通過する各パスの終わりにその状態を評価します。

while

while condition {
    statements
}

repeat-while

repeat {
    statements
} while condition

条件文

Swiftには、コードに条件分岐を追加する2つの方法があります。ifステートメントとswitchステートメントです。

if

temperatureInFahrenheit = 90
if temperatureInFahrenheit <= 32 {
    print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
    print("It's really warm. Don't forget to wear sunscreen.")
} else {
    print("It's not that cold. Wear a t-shirt.")
}

switch

switch some value to consider {
case value 1:
    respond to value 1
case value 2,
     value 3:
    respond to value 2 or 3
default:
    otherwise, do something else
}

暗黙のフォールスルーなし

Swiftのステートメントは、各ケースの最下部をfall through せず、次のケースにすすみません。switchでは、明示的なbreakステートメントを必要とせずに、最初の一致するケースが完了するとすぐに実行を終了します。これにより、switchステートメントがC のステートメントよりも安全で使いやすくなり、誤って複数のケースを実行することが回避されます。break は、Swiftでは必要とされていませんが、使用することができます。

各ケースの本文には、少なくとも1つの実行可能ステートメントが含まれている必要あります。

switch anotherCharacter {
case "a": // 実行文がないため、エラーになる
case "A":
    print("The letter A")
default:
    print("Not the letter A")
}

このような場合は、次のように、ケースをコンマで区切ります。
switch anotherCharacter {
case "a", "A":
    print("The letter A")
default:
    print("Not the letter A")
}

switchで、特定のケースの最後に明示的にフォールスルーするには、fallthroughキーワードを使用します。

インターバルマッチング

ケースの値は、interval に含まれているかどうかを確認できます。

switch approximateCount {
  case 0:
    naturalCount = "no"
  case 1..<5:
    naturalCount = "a few"
  case 5..<12:
    naturalCount = "several"
  default:
     naturalCount = "many"
}

タプル

タプルを使用して、同じステートメントで複数の値をテストできます。タプルの各要素は、異なる値または値の間隔に対してテストできます。または、アンダースコア文字を使用して、可能な値と一致させます。

let somePoint = (1, 1)
switch somePoint {
  case (0, 0):
    print("\(somePoint) is at the origin")
  case (_, 0):
    print("\(somePoint) is on the x-axis")
  case (0, _):
    print("\(somePoint) is on the y-axis")
  case (-2...2, -2...2):
    print("\(somePoint) is inside the box")
  default:
    print("\(somePoint) is outside of the box")
}

value binding

case内で、一時的な定数や変数に一致する値に名前を付けることができます。この動作はvalue vindingと呼ばれます

let anotherPoint = (2, 0)
switch anotherPoint {
  case (let x, 0):
    print("on the x-axis with an x value of \(x)")
  case (0, let y):
    print("on the y-axis with a y value of \(y)")
  case let (x, y):
    print("somewhere else at (\(x), \(y))")
}

where

switchでは、where で条件をチェックする句を追加できる。

let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
  case let (x, y) where x == y:
    print("(\(x), \(y)) is on the line x == y")
  case let (x, y) where x == -y:
    print("(\(x), \(y)) is on the line x == -y")
  case let (x, y):
    print("(\(x), \(y)) is just some arbitrary point")
}

複合case

// case では、複数の値をコンマで記述することができます
// リストが長い場合、パターンは複数行に渡って記述できます
switch someCharacter {
  case "a", "e", "i", "o", "u":
    print("\(someCharacter) is a vowel")
  case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
            "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
    print("\(someCharacter) is a consonant")
  default:
    print("\(someCharacter) is not a vowel or a consonant")
}

// value binding の場合
switch stillAnotherPoint {
  case (let distance, 0), (0, let distance):
    print("On an axis, \(distance) from the origin")
  default:
    print("Not on an axis")
}

Control Transfer 文

Control Transfer 文は、あるコードから別のコードに制御を転送することにより、コードが実行される順序変更します。Swiftには5つの制御転送ステートメントがあります。

  • continue
  • break
  • fallthrough
  • return
  • throw

continue

continue文は、ループに実行中の処理を停止し、ループの次の反復の開始時に再び開始するように指示します。

let puzzleInput = "great minds think alike"
var puzzleOutput = ""
let charactersToRemove: [Character] = ["a", "e", "i", "o", "u", " "]
for character in puzzleInput {
  if charactersToRemove.contains(character) {
    continue
  }
  puzzleOutput.append(character)
}
print(puzzleOutput)
// Prints "grtmndsthnklk"

break

breakステートメントは、制御フローステートメント全体の実行をすぐに終了します。

ループ文内の break文

ループステートメント内で break を使用すると、ループの実行がすぐに終了し、ループの閉じ中かっこの後に制御がコードに移ります。

switchステートメントの中断

switch 内部で使用する場合、すぐにその実行を終了し、} 後のコードに制御を写す。

let numberSymbol: Character = "三" // Chinese symbol for the number 3
var possibleIntegerValue: Int?
switch numberSymbol {
  case "1", "١", "一", "๑":
    possibleIntegerValue = 1
  case "2", "٢", "二", "๒":
    possibleIntegerValue = 2
  case "3", "٣", "三", "๓":
    possibleIntegerValue = 3
  case "4", "٤", "四", "๔":
    possibleIntegerValue = 4
  default:
    break
}
if let integerValue = possibleIntegerValue {
  print("The integer value of \(numberSymbol) is \(integerValue).")
} else {
  print("An integer value could not be found for \(numberSymbol).")
}
// Prints "The integer value of 三 is 3."

fallthrough

Swiftでは、switchステートメントが各ケースの下部から次のケースに落ちることはありません。つまりswitch、最初の一致するケースが完了するとすぐに、ステートメント全体が実行を完了します。対照的に、Cでは、フォールスルーを防ぐためにbreak、すべてのswitchケースの最後に明示的なステートメントを挿入する必要があります。これにより、誤って複数のケースを実行することを回避します。

Cスタイルのフォールスルー動作が必要な場合は、fallthroughキーワードを使用します。

let integerToDescribe = 5
var description = "The number \(integerToDescribe) is"
switch integerToDescribe {
  case 2, 3, 5, 7, 11, 13, 17, 19:
    description += " a prime number, and also"
    fallthrough
  default:
    description += " an integer."
}
print(description)
// Prints "The number 5 is a prime number, and also an integer."

注意

このfallthroughキーワードは、実行に該当するケースのケース条件をチェックしません。これは、c言語と同じ振る舞いです。

ラベル付きステートメント

label name: while condition {
    statements
}

// 例
gameLoop: while square != finalSquare {
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    switch square + diceRoll {
      case finalSquare:
          // gameLoop を終わらせる
         break gameLoop 
      case let newSquare where newSquare > finalSquare:
         // gameLoop を続ける
        continue gameLoop
    default:
        break
   }
}

注意

上記の例で、ラベルを使用しなかった場合、switch の break, continue として扱われる。ラベルを使用すると、終了する必要のある制御ステートメントが明確になります。

早期終了

guardステートメントの後のコードを実行するためには、guard 条件が真でなければならない。if文とは異なり、guard文には常にelse句があります。条件が真でない場合、句内のコードが実行されます。

 func guardTest(status: Int) {
    guard status == 0 else { return }
    print("status == 0 である")
}

guardステートメントがコードブロックを終了するために、returnbreakcontinue、またはthrow、fatalError(_:file:line:)のような、関数やメソッドを呼び出すことができる。
また、早期終了処理は、if文で書くより可読性が良い。

APIの可用性の確認

APIの可用性を確認するためのサポートが組み込まれているため、特定のデプロイメントターゲットで使用できないAPIを誤って使用することはありません。

コンパイラーはSDKの可用性情報を使用して、コードで使用されているすべてのAPIが、プロジェクトで指定されたデプロイメントターゲットで使用できることを確認します。利用できないAPIを使おうとすると、Swiftはコンパイル時にエラーを報告します。

if または guard文で、使用するAPIが実行時に使用できるかどうかに応じて、コードブロックを条件付きで実行できる。

if #available(iOS 10, macOS 10.12, *) {
    // iOS では10以降のみ、macOSでは 10.12以降のみ、及びそれ以外のプラットフォーム
} else {
    // Fall back to earlier iOS and macOS APIs
}

コレクションの種類

Swiftは、配列、セット、および辞書の3つのコレクションタイプを提供します。配列は、順序付けられた値のコレクションです。セットは、一意の値の順序付けられていないコレクションです。辞書は、キーと値の関連付けの順序付けられていないコレクションです。

../_images/CollectionTypes_intro_2x.png

コレクションの可変性

配列、セット、または辞書を作成し、それを変数に割り当てると、作成されるコレクションは変更可能になります。コレクションを変更する必要がない場合に、let による不変コレクションを作成することを勧める。そうすることで、コードを推論しやすくなり、コンパイラーがパフォーマンスを最適化できる。

配列

配列の順序付きリスト内の同じタイプの値を格納する。NSArrayに橋渡しされている。

配列型の省略構文

Array<Element> ここで、Elementは、配列に格納できる値のタイプ。

省略形:[Element]

空の配列の作成

初期化構文を使用して、特定のタイプの空の配列を作成できる。

var someInts = [Int]()

デフォルト値を持つ配列の作成

すべての値が同じデフォルト値で、特定のサイズの配列の作成。

var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]

2つの配列を一緒に追加して配列を作成する

加算演算子(+)を使用して、互換性のあるタイプの2つの既存の配列を加算することにより、新しい配列を作成できます。新しい配列のタイプは、一緒に追加する2つの配列のタイプから推測されます。

var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles is of type [Double], and equals [2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles is inferred as [Double], and equals [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]

配列リテラルを使用した配列の作成

また、配列リテラルで配列を初期化することもできます。これは、1つ以上の値を配列コレクションとして書き込むための簡単な方法です。配列リテラルは、コンマで区切られた値のリストとして記述され、角括弧のペアで囲まれます。

[value 1, value 2, value 3]

// 例
var shoppingList: [String] = ["Eggs", "Milk"]
// shoppingList has been initialized with two initial items

// 型推論により、次のように記述できる。
var shoppingList = ["Eggs", "Milk"]

配列へのアクセスと変更

メソッドとプロパティを介して、または添字構文を使用して、配列にアクセスして変更します。

// 配列内のアイテム数には、読み取り専用countプロパティ
print("The shopping list contains \(shoppingList.count) items.")

// 配列が空かどうかは、isEmpty プロパティ
if shoppingList.isEmpty {
    print("The shopping list is empty.")
} else {
    print("The shopping list is not empty.")
}

// 配列の最後に新しいアイテムを追加
shoppingList.append("Flour")

// 追加の代入演算子 += 1つ以上の互換性のあるアイテムの配列を追加
shoppingList += ["Baking Powder"]
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]

// インデックス
// 配列の最初の項目のインデックスは0
var firstItem = shoppingList[0]

// 特定のインデックスにある既存の値を変更
shoppingList[0] = "Six eggs"

// 次はランタイムエラー
shoppingList[shoppingList.count] = "Salt"

// 配列の長さが置換する長さと異なっていても、一度に変更できる。
// 次の場合は、"Baking Powder", "ChocolateSpread", "Cheese", "Butter", "Bananas", "Apples" となる。
shoppingList[4...6] = ["Bananas", "Apples"]

// 配列の指定したインデックスにアイテムを挿入
shoppingList.insert("Maple Syrup", at: 0)

// 指定されたインデックスにある項目を削除し、削除された項目を返す
let mapleSyrup = shoppingList.remove(at: 0)

// 最初のアイテム
firstItem = shoppingList[0]

// 配列から最後の項目を削除する場合
// 配列のcountプロパティを確認する必要がない
let apples = shoppingList.removeLast()

配列の反復

for– inループを使用して、配列内の値のセット全体を反復処理できます。

for item in shoppingList {
    print(item)
}

// 各アイテムの整数インデックスとその値が必要な場合はenumerated()
for (index, value) in shoppingList.enumerated() {
    print("Item \(index + 1): \(value)")
}

セット

一意の値の順序付けられていないコレクション。

アイテムの順序が重要ではない場合、またはアイテムが1回だけ表示されるようにする必要がある場合は、配列の代わりにセットを使用できます。NSSetに橋渡しされている。

セット型のハッシュ値

セットに格納するためには、hashable型である必要がある。

String, Int, Double, Boolなどの基本的なタイプは、デフォルトでハッシュ可能であり、セットの値の型または辞書のキータイプとして使用することができる。関連する値のない列挙型のケース値も、デフォルトでハッシュ可能。

タイプ構文の設定

Set<Element>

配列とは異なり、セットには省略形はありません。

空のセットの作成と初期化

// 初期化構文
var letters = Set<Character>()

// 挿入
letters.insert("a")

// 空のセット
letters = []

配列リテラルを使用したセットの作成

// 配列リテラルでセットを初期化
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]

// 1つの型のみの配列リテラルで初期化する場合は、型は必要はありません
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]

セットへのアクセスと変更

// アイテム数
print("I have \(favoriteGenres.count) favorite music genres.")

// 空かどうか
if favoriteGenres.isEmpty {
    print("空のセットです")
}

// 新しいアイテムをセットに追加
favoriteGenres.insert("Jazz")

// セットからアイテムを削除
// 削除された値を返す
if let removedGenre = favoriteGenres.remove("Rock") {
    print("\(removedGenre)? I'm over it.")
} else {
    print("I never much cared for that.")
}

// セットに特定のアイテムが含まれているかどうかを確認
favoriteGenres.contains("Funk") 

セットの反復

// for- inループを使用すると、セットの値を反復処理
for genre in favoriteGenres {
    print("\(genre)")
}

// 特定の順序でセットの値を反復処理
for genre in favoriteGenres.sorted() {
    print("\(genre)")
}

集合演算の実行

基本セット演算

../_images/setVennDiagram_2x.png

let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]

メンバーシップと平等を設定する

../_images/setEulerDiagram_2x.png

  • 「等しい」演算子(==)を使用して、2つのセットに同じ値がすべて含まれているかどうかを判断します。
  • isSubset(of:)メソッドを使用して、セットのすべての値が指定されたセットに含まれているかどうかを判別します。
  • isSuperset(of:)メソッドを使用して、指定したセットのすべての値がセットに含まれているかどうかを確認します。
  • isStrictSubset(of:)or isStrictSuperset(of:)メソッドを使用して、セットがサブセットであるかスーパーセットであるかを指定しますが、指定されたセットとは異なります。
  • isDisjoint(with:)メソッドを使用して、2つのセットに共通の値がないかどうかを判別します。
let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]
houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSuperset(of: houseAnimals)
// true
farmAnimals.isDisjoint(with: cityAnimals)
// true

辞書

辞書は、同じ型のキーと同じ型の値の関連を、順序なしコレクションとして格納します。各値は一意のキーに関連づけられる。NSDictionaryクラスに橋渡されている。

辞書型の短縮構文

Dictionary<Key, Value>KeyValue

省略形: [Key: Value]

KeyはHashable

空の辞書を作成する

// 特定のタイプの空の辞書
var namesOfIntegers = [Int: String]()

// 1つのキー値ペアを作成
namesOfIntegers[16] = "sixteen"

// 空のディクショナリ
namesOfIntegers = [:]

辞書リテラルを使用した辞書の作成

辞書リテラルで辞書を初期化することもできます。これは、前に見た配列リテラルと同様の構文です。辞書リテラルは、1つ以上のキーと値のペアをDictionaryコレクションとして記述するための簡単な方法です。

// 国際空港の名前を格納する辞書
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]

// キーと値の型が一致する辞書リテラルで辞書を初期化する場合、型を記述する必要はありません
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]

辞書へのアクセスと変更

// アイテム数
airports.count

// 空かどうか
airports.isEmpty

// 追加
airports["LHR"] = "London
// 変更
airports["LHR"] = "London Heathrow"

// 特定のキーの値を設定・更新
// すでにキーが存在する場合、古い値を返す
// 存在しない場合、nil を返す
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
     print("The old value for DUB was \(oldValue).")
}

// 特定のキーのディクショナリから値を取得
// すでにキーが存在する場合、値を返す
// 存在しない場合、nil を返す
if let airportName = airports["DUB"] {
    print("The name of the airport is \(airportName).")
} 

// キーに値を設定
airports["APL"] = "Apple International"
// 値を削除
airports["APL"] = nil

// キーと値のペアを削除
// 削除された値を返す
// 存在しない場合、nil を返す
if let removedValue = airports.removeValue(forKey: "DUB") {
    print("The removed airport's name is \(removedValue).")
}

辞書の反復

for– inループを使用して、ディクショナリ内のキーと値のペアを反復できます。ディクショナリ内の各アイテムはタプルとして返され、反復の一部としてタプルのメンバーを一時的な定数または変数に分解できます。(key, value)

// for- inループを使用して、ディクショナリ内のキーと値のペアを反復
// 各アイテムはタプルとして返される
for (airportCode, airportName) in airports {
    print("\(airportCode): \(airportName)")
}

// keys プロパティにアクセス
for airportCode in airports.keys {
    print("Airport code: \(airportCode)")
}

// values プロパティにアクセス
for airportName in airports.values {
    print("Airport name: \(airportName)")
}

// keys プロパティでアクセス
let airportCodes = [String](airports.keys)
// airportCodes is ["LHR", "YYZ"]

// values プロパティでアクセス
let airportNames = [String](airports.values)
// airportNames is ["London Heathrow", "Toronto Pearson"]

Dictionary型には順序が定義されていません。ディクショナリのキーまたは値を特定の順序で反復するには、そのkeysor valuesプロパティへのsorted()メソッドを使用します。

文字列と文字

すべての文字列は、エンコーディングに依存しないUnicode文字で構成されている。

注意

Swift の String 型は、Foundation の NSString classへ橋渡しされており、Foundation をインポートすると、NSString のメソッドをキャストせずに使うことができる。

文字列リテラル

文字列リテラルは、二重引用符(")で囲まれた文字のシーケンスです。

let someString = "Some string literal value"

複数行文字列リテラル

複数行にまたがる文字列が必要な場合は、複数行の文字列リテラル(”””)を使用します。

let quotation = """
The White Rabbit put on his spectacles.  "Where shall I begin,
please your Majesty?" he asked.

"Begin at the beginning," the King said gravely, "and go on
till you come to the end; then stop."
"""

// ソースコードを読みやすくするために改行を使用したいが、改行を文字列の値の一部にしたくない場合は、バックスラッシュを用いる。
let softWrappedQuotation = """
The White Rabbit put on his spectacles. "Where shall I begin, \
please your Majesty?" he asked.
"Begin at the beginning," the King said gravely, "and go on \
till you come to the end; then stop."
"""

// 改行で開始または終了する複数行の文字列リテラル
let lineBreaks = """

This string starts with a line break.
It also ends with a line break.

"""

複数行の文字列をインデントして、周囲のコードと一致させることができます。../_images/multilineStringWhitespace_2x.png

文字列リテラルの特殊文字

文字列リテラルには、次の特殊文字を含めることができます。

  • エスケープされた特殊文字\0(null文字)、\\(バックスラッシュ)、\t(水平タブ)、\n(改行)、\r(キャリッジリターン)、\"(二重引用符)および\'(単一引用符)
  • 以下のように記述任意のUnicodeスカラー値、\u{nは}nは 1〜8桁の16進数
// エスケープされたダブルクオート
let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imagination is more important than knowledge" - Einstein

// Unicode 
let dollarSign = "\u{24}" 
// $, Unicode scalar U+0024

let blackHeart = "\u{2665}" 
// ♥, Unicode scalar U+2665

let sparklingHeart = "\u{1F496}" 
// 💖, Unicode scalar U+1F496

複数行文字列リテラルは1つではなく3つの二重引用符を使用するため、"""複数行の文字列にテキストを含めるには、少なくとも1つの引用符をエスケープします。

let threeDoubleQuotationMarks = """
Escaping the first quotation mark \"""
Escaping all three quotation marks \"\"\"
"""
// 結果
// Escaping the first quotation mark """
// Escaping all three quotation marks """

拡張文字列区切り文字

文字列リテラルを拡張区切り文字内に配置して、特殊文字を文字列に含めることができます。

let threeMoreDoubleQuotationMarks = #"""
Here are three more double quotes: """
"""#
// Here are three more double quotes: """

let teststr = #"Line 1\nLine 2"#
 print(teststr)
// Line 1\nLine 2

空の文字列の初期化

空の文字列の初期化には、空の文字列リテラルを変数に割り当てるか、イニシャライザ構文で新しいインスタンスを初期化する。

var emptyString = "" // 空の文字列
var anotherEmptyString = String() // 空の文字列

文字列の可変性

特定のかどうかを示すString変更された(またはされ得る変異、または(それは変更することができない場合)定数(この場合、それは変更することができる)変数に割り当てることによって)。

var variableString = "Horse"
variableString += " and carriage"
// 変更可能

let constantString = "Highlander"
constantString += " and another Highlander"
// 定数は変更できず、コンパイル時エラー

文字列は値型

String は値方です。Stringの値は、関数またはメソッドに渡されるとき、または定数または変数に割り当てられるときにコピーされる。

キャラクターの操作

ループで文字列を反復することにより、個々の文字にアクセスできます。
for character in "Dog!🐶" {
     print(character)
}
// D
// o
// g
// !
// 🐶

// Character タイプアノテーションにより、文字列から文字を得る。
let exclamationMark: Character = "!"

// Stringに、Characterの配列を渡すことにより、文字列を得る。
let catCharacters: [Character] = ["C", "a", "t", "!", "🐱"]
let catString = String(catCharacters)
print(catString)
// Prints "Cat!🐱"

文字列と文字列の連結

String値は、追加演算子()を使用して一緒に追加(または連結)して+、新しいString値を作成できます。

// + による連結
let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
// welcome now equals "hello there"

// += による連結
var instruction = "look over"
instruction += string2
// instruction now equals "look over there"

// String.appendによる連結
let exclamationMark: Character = "!"
welcome.append(exclamationMark)
// welcome now equals "hello there!"

複数行の文字列リテラルを使用して長い文字列の行を作成する場合は、最後の行を含め、文字列のすべての行を改行で終了する必要がある。例えば:

let badStart = """
one
two
"""
let end = """
three
"""
print(badStart + end)
// Prints two lines:
// one
// twothree

let goodStart = """
one
two

"""
print(goodStart + end)
// Prints three lines:
// one
// two
// three

文字列補間

文字列補間は、定数、変数、リテラル、および式の組み合わせから値を文字列リテラル内に含めることにより、新しい値を構築する。文字列補間は、単一行と複数行の両方の文字列リテラルで使用できます。

バックスラッシュは、Option キー + ”¥” キーで入力する。

let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message is "3 times 2.5 is 7.5"

// 拡張文字列区切り文字を使用して、文字列補間の文字列を作成できる
print(#"Write an interpolated string in Swift using \(multiplier)."#)
// Prints "Write an interpolated string in Swift using \(multiplier)."

// 拡張区切り文字を使用する文字列内で文字列補間を使用するには、バックスラッシュの後のシャープ記号の数を、文字列の先頭と末尾のシャープ記号の数と一致させる。
print(#"6 times 7 is \#(6 * 7)."#)
// Prints "6 times 7 is 42."

Unicode

Unicodeスカラー値

Swiftの String 型はUnicodeスカラー値から構築されています。Unicodeスカラー値は、一意の21ビット数値です。

U+0061 は、 LATIN SMALL LETTER A ("a")
U+1F425 は、FRONT-FACING BABY CHICK ("🐥").

すべての21ビットUnicodeスカラー値が文字に割り当てられるわけではないことに注意してください。一部のスカラーは、将来の割り当てまたはUTF-16エンコーディングでの使用のために予約されています。文字に割り当てられているスカラー値には、通常、上記の例やなどの名前も付いています。

Extended Grapheme Clusters

SwiftのCharacter型のすべてのインスタンスは、単一のextended grapheme cluster表しますextended grapheme cluster は、1つ以上のUnicodeスカラーのシーケンスであり、組み合わせて、人間が読める単一の文字を生成します。

例えば、文字éは、単一のUnicodeスカラーé はé (LATIN SMALL LETTER E WITH ACUTE, or U+00E9)として表すことができます。
また、同じ文字を1 のスカラー 標準の文字 e (LATIN SMALL LETTER E, or U+0065), 及びそれに続く COMBINING ACUTE ACCENT scalar (U+0301) として表すこともできます。

どちらの場合も、文字éは、extended grapheme cluster を表す単一の値として表されます。最初のケースでは、クラスターには単一のスカラーが含まれています。2番目のケースでは、2つのスカラーのクラスターです。

let eAcute: Character = "\u{E9}" // é
let combinedEAcute: Character = "\u{65}\u{301}" 
// e followed by eAcute is é, combinedEAcute is é

// ハングルの例
let precomposed: Character = "\u{D55C}" // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ
// precomposed is 한, decomposed is 한

let enclosedEAcute: Character = "\u{E9}\u{20DD}"
// 丸の中にé

// 地域インジケーターシンボルの例
let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS is 🇺🇸

文字を数える

Character文字列の値の数を取得するには、文字列のcountプロパティを使用します。

let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
print("unusualMenagerie has \(unusualMenagerie.count) characters")
// Prints "unusualMenagerie has 40 characters"

SwiftがCharacter値にextended grapheme cluster を使用するということは、文字列の連結と変更が常に文字列の文字数に影響を与えるとは限らないことに注意してください。

たとえば、新しい文字列を4文字の単語cafeで初期化してから、文字列の末尾にCOMBINING ACUTE ACCENTU  (U+0301) を追加しても4文字となる。

var word = "cafe"
print("the number of characters in \(word) is \(word.count)")
// Prints "the number of characters in cafe is 4"
word += "\u{301}" // COMBINING ACUTE ACCENT, U+0301
print("the number of characters in \(word) is \(word.count)")
// Prints "the number of characters in café is 4"

文字列へのアクセスと変更

文字列インデックス

String.Indexは、文字列のそれぞれの位置に対応した文字です。

上記のように、文字ごとに格納するメモリの量が異なる可能性があるためCharacter、特定の位置にある文字を特定するには、その先頭または末尾から各Unicodeスカラーを反復処理する必要があります。このため、Swift文字列は整数値でインデックスを作成できません。

let greeting = "Guten Tag!"
greeting[greeting.startIndex]
// G
greeting[greeting.index(before: greeting.endIndex)]
// !
greeting[greeting.index(after: greeting.startIndex)]
// u
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index]
// a

startIndex:最初Characterの位置
endIndex:最後の文字の1つ後の位置

文字列の範囲Character外のインデックスまたは文字列の範囲外のインデックスにアクセスしようとすると、ランタイムエラーが発生します。

greeting[greeting.endIndex] // Error
greeting.index(after: greeting.endIndex) // Error

indicesプロパティを使用して、文字列内の個々の文字のすべてのインデックスにアクセス。

for index in greeting.indices {
    print("\(greeting[index]) ", terminator: "")
}
// Prints "G u t e n T a g ! "

挿入と削除

文字列の指定したインデックスに単一の文字を挿入するには、insert(_:at:)メソッドを使用し、指定したインデックスに別の文字列の内容を挿入するには、insert(contentsOf:at:)メソッドを使用します。

var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome now equals "hello!"
welcome.insert(contentsOf: " there", at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there!"

remove(at:):指定されたインデックスの文字列から1文字を削除
removeSubrange(_:):指定された範囲の部分文字列を削除

welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there"
let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome now equals "hello"

Substring

String.firstIndex(of:)
String.endIndex(of:)

let greeting = "Hello, world!"
let index = greeting.firstIndex(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index]
// beginning is "Hello"
// Convert the result to a String for long-term storage.
let newString = String(beginning)

上記の例でgreetingは、文字列です。これは、文字列を構成する文字が格納されるメモリ領域があることを意味します。beginningはSubstringなので、メモリを再利用します。対照的に、newStringは文字列です。Substringから作成された場合、独自のストレージを持っています。次の図は、これらの関係を示しています。../_images/stringSubstring_2x.png

文字列の比較

Swiftは、テキスト値を比較する3つの方法を提供する。

  • 文字列と文字の等価性
  • プレフィックスの等価性
  • サフィックスの等価性。

文字列と文字の等価性

比較演算子で説明されているように、文字列と文字の等価性は、== 演算子と!=演算子でチェックされます。

let quotation = "We're a lot alike, you and I."
let sameQuotation = "We're a lot alike, you and I."
if quotation == sameQuotation {
    print("等しい")
}
// Prints "等しい"

2つのString値(または2つのCharacter値)は、Extended Grapheme Clusters が標準的に等価である場合、等しいと見なされます。Extended Grapheme Clustersは、それらが舞台裏で異なるUnicodeスカラーから構成されている場合でも、同じ言語的意味と外観を持っている場合、標準的に同等です。

例えば、
 LATIN SMALL LETTER E WITH ACUTE (U+00E9)
LATIN SMALL LETTER E (U+0065) followed by COMBINING ACUTE ACCENT (U+0301)
これらのExtended Grapheme Clustersはどちらも、é文字を表現する有効な方法であるため、同等であると見なされます。

// "Voulez-vous un café?" using LATIN SMALL LETTER E WITH ACUTE
let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"
// "Voulez-vous un café?" using LATIN SMALL LETTER E and COMBINING ACUTE ACCENT
let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"
if eAcuteQuestion == combinedEAcuteQuestion {
print("These two strings are considered equal")
}
// Prints "These two strings are considered equal"

逆に、
LATIN CAPITAL LETTER A (U+0041, or "A") 英語で使用される
CYRILLIC CAPITAL LETTER A (U+0410, or "А") ロシア語で使用される
は、視覚的には似ていますが、同じ言語的意味はありません。

let latinCapitalLetterA: Character = "\u{41}"
let cyrillicCapitalLetterA: Character = "\u{0410}"
if latinCapitalLetterA != cyrillicCapitalLetterA {
print("These two characters are not equivalent.")
}
// Prints "These two characters are not equivalent."

注意

Swiftでの文字列と文字の比較は、ロケールに依存しません。

接頭辞と接尾辞が等しい

文字列に特定の文字列の接頭辞または接尾辞があるかどうかを確認するには、文字列のhasPrefix(_:)およびhasSuffix(_:)メソッドを使用する。

let romeoAndJuliet = [
"Act 1 Scene 1: Verona, A public place",
"Act 1 Scene 2: Capulet's mansion",
"Act 1 Scene 3: A room in Capulet's mansion",
"Act 1 Scene 4: A street outside Capulet's mansion",
"Act 1 Scene 5: The Great Hall in Capulet's mansion",
"Act 2 Scene 1: Outside Capulet's mansion",
"Act 2 Scene 2: Capulet's orchard",
"Act 2 Scene 3: Outside Friar Lawrence's cell",
"Act 2 Scene 4: A street in Verona",
"Act 2 Scene 5: Capulet's mansion",
"Act 2 Scene 6: Friar Lawrence's cell"
]

var act1SceneCount = 0
for scene in romeoAndJuliet {
if scene.hasPrefix("Act 1 ") {
act1SceneCount += 1
}
}
print("There are \(act1SceneCount) scenes in Act 1")
// Prints "There are 5 scenes in Act 1"

var mansionCount = 0
var cellCount = 0
for scene in romeoAndJuliet {
if scene.hasSuffix("Capulet's mansion") {
mansionCount += 1
} else if scene.hasSuffix("Friar Lawrence's cell") {
cellCount += 1
}
}
print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
// Prints "6 mansion scenes; 2 cell scenes"

文字列のUnicode表現

Unicode文字列がテキストファイルまたはその他のストレージに書き込まれると、その文字列内のUnicodeスカラーは、コード単位と呼ばれる小さなチャンクにエンコードされます。これらには、UTF-8エンコード形式、UTF-16エンコード形式、UTF-32エンコード形式がある。

Swiftは、文字列のUnicode表現にアクセスするためのいくつかの異なる方法を提供します。for– inステートメントを使用して文字列を反復処理し、Unicode extended grapheme clustersとして個々の値にアクセスできます。

または、String他の3つのUnicode準拠の表現のいずれかの値にアクセスします。

  • UTF-8コード単位のコレクション(文字列のutf8プロパティでアクセス)
  • UTF-16コード単位のコレクション(文字列のutf16プロパティでアクセス)
  • 21ビットUnicodeスカラー値のコレクション。文字列のUTF-32エンコード形式(文字列のunicodeScalarsプロパティでアクセスされる)と同等

D, o, g, ‼ (DOUBLE EXCLAMATION MARK, or Unicode scalar U+203C), と 🐶  (DOG FACE, or Unicode scalar U+1F436):

let dogString = "Dog‼🐶"

UTF-8表現

../_images/UTF8_2x.png

for codeUnit in dogString.utf8 {
print("\(codeUnit) ", terminator: "")
}
print("")
// Prints "68 111 103 226 128 188 240 159 144 182 "

UTF-16表現

../_images/UTF16_2x.png

for codeUnit in dogString.utf16 {
print("\(codeUnit) ", terminator: "")
}
print("")
// Prints "68 111 103 8252 55357 56374 "

Unicodeスカラー表現

../_images/UnicodeScalar_2x.png

for scalar in dogString.unicodeScalars {
print("\(scalar.value) ", terminator: "")
}
print("")
// Prints "68 111 103 8252 128054 "

valueプロパティをクエリする代わりに、各UnicodeScalar値を使用して、文字列補間などの新しい値を作成することもできます。

for scalar in dogString.unicodeScalars {
print("\(scalar) ")
}
// D
// o
// g
// ‼
// 🐶

基本的な演算子

代入演算子

let b = 10
var a = 5
a = b

// タプル
let (x, y) = (1, 2

算術演算子

Swiftは、すべての数値タイプに対して4つの標準算術演算子をサポートしています。

  • 加算(+
  • 減算(-
  • 乗算(*
  • 除算(/

CおよびObjective-Cの算術演算子とは異なり、Swift算術演算子では、デフォルトで値がオーバーフローすることはない。

文字列も加算可能。
“hello, ” + “world”

剰余演算子

a % b

9 % 4 // equals 1
-9 % 4   // equals -1

単項マイナス演算子

let three = 3
let minusThree = -three // -3

単項プラス演算子

let minusSix = -6
let alsoMinusSix = +minusSix // 単項プラス演算子は実際には何もしません

複合代入演算子

var a = 1
a += 2 // 3
let b = a += 2  // エラー。複合代入演算子は値を返さないので、エラーになる。

比較演算子

a == b  // 等しい
a != b // 等しくない
a > b // より大きい
a < b // より小さい
a >= b // 以上
a <= b // 以下

Swiftには2つのID演算子===および!==)も用意されている。これを使用して、2つのオブジェクト参照が両方とも同じオブジェクトインスタンスを参照しているかどうかをテストできる。詳細については、「アイデンティティオペレーター」を参照。

タプルの比較

2つのタプルのタイプと値の数が同じである場合、比較できる。
タプルは、比較で2つの値が等しくないことが見つかるまで、左から右に、一度に1つの値が比較されます。これらの2つの値が比較され、その比較の結果がタプル比較の全体的な結果となる。

(1, "zebra") < (2, "apple") 
// true 
// 1 < 2 なので比較を終了、"zebra" と "apple" は比較されない

(3, "apple") < (3, "bird") 
// true
// 3==3 なので比較を継続、"apple" < "bird"

(4, "dog") == (4, "dog") 
// true 
// 4==4 なので比較を継続、"dog" == "dog"

("blue", -1) < ("purple", 1) 
// true

("blue", false) < ("purple", true) 
// エラー。ブーリアンを比較できない。

注意

Swift標準ライブラリには、7要素未満のタプル用のタプル比較演算子が含まれています。7つ以上の要素を持つタプルを比較するには、比較演算子を自分で実装する必要がある。

三項条件乗演算子

question ? answer1 : answer2questionquestionanswer1answer2

三項条件演算子は、以下のコードの省略形です。

if question {
    answer1
} else {
    answer2
}

// 例
let hasHeader = true
let rowHeight = hasHeader ? 50 : 20
// rowHeight is equal to 50

nil結合演算子

a ?? b

a が nil でなければ アンラップしたaの値、aがnilであれば bを返す。
式は常にオプショナルタイプ。

nil 結合演算子は、以下のコードの省略形です。

a != nil ? a! : b

注意

aの値がnilではない場合、b の値は評価されません。

範囲演算子

Closed Range 演算子

a…b

for index in 1...5 {
    print("\(index) times 5 is \(index * 5)")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25

Half-Open Range 演算子

a..<b

let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
     print("Person \(i + 1) is called \(names[i])")
}
// Person 1 is called Anna
// Person 2 is called Alex
// Person 3 is called Brian
// Person 4 is called Jack

One-Sided Ranges 演算子

for name in names[2...] {
    print(name)
}
// Brian
// Jack

for name in names[...2] {
    print(name)
}
// Anna
// Alex
// Brian

for name in names[..<2] {
    print(name)
}
// Anna
// Alex

let range = ...5
range.contains(7) // false
range.contains(4) // true
range.contains(-1) // true

論理演算子

Swiftは、Cベースの言語にある3つの標準論理演算子をサポートしています。

  • 論理否定 !a
  • 論理AND a && b
  • 論理OR a || b

論理否定演算子

論理否定演算子は !a のようにブール値を反転する。

論理AND演算子

a && b

最初の値がのfalse場合、2番目の値は評価されません。

let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
// Prints "ACCESS DENIED"

論理OR演算子

a || b

let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
// Prints "Welcome!"

論理演算子の組み合わせ

複数の論理演算子を組み合わせて、より長い複合式を作成できます。

if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
// Prints "Welcome!"

複数の論理演算子を持つ複合式は、左端の部分式を最初に評価します。

明示的な括弧

複雑な式の意図を読みやすくするために、括弧を含めると良い。
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
// Prints "Welcome!"

Basics

定数と変数

// 定数
let maximumNumberOfLoginAttempts = 10 

// 変数
var currentLoginAttempt = 0

// 複数の定数または複数の変数は、コンマ区切り
var x = 0.0, y = 0.0, z = 0.0

// 型宣言
var welcomeMessage: String

// 複数の変数の型宣言
var red, green, blue: Double

// 定数と変数の命名
let π = 3.14159
let 日本語 = "日本語の変数名"
let 🐶🐮 = "dogcow"

// 定数と変数の印刷
let helloWorld = "Hello World!"
print(helloWorld)
print("\(helloWorld) こんにちは")

コメント

複数の定数または複数の変数を、コンマで区切って1行で宣言できます。

// C++ Style
// This is a comment.

// C Style
/* This is also a comment
but is written over multiple lines. */

// ネストされたコメント
/* Cの複数行コメントとは異なり、
 /* Swiftの複数行コメントは他の複数行コメント内にネストできます。 */
コメント入りのコードをコメントアウトすることができる */

セミコロン

// 1行に複数の個別のステートメントを記述したい場合
let cat = "🐱"; print(cat)

整数

Intは、32ビットプラットフォームではInt32、64ビットプラットフォームではUInt64。

UIntは、32ビットプラットフォームではUInt32、64ビットプラットフォームではUInt64。

浮動小数点数

Double 64ビット浮動小数点数。通常は、こちらを推奨。

Float 32ビット浮動小数点数。

Type Safe と型推論

Swift は Type Safe。

浮動小数点は、既定でDoubleとなる。
let pi = 3.14159

Numeric リテラル

// 十進数
let decimalInteger = 17

// 二進数
let binaryInteger = 0b10001 

// 八進数
let octalInteger = 0o21

// 十六進数
let hexadecimalInteger = 0x11

// 浮動小数点
let decimalDouble = 12.1875

// 十進数は、10exp。
let exponentDouble = 1.21875e1 // これは、12.1875

// 十六進数は、2exp
let hexadecimalDouble = 0xC.3p0 // これは、12.3 の2の0乗

// 整数と浮動小数点では、0が先行しても良い
let paddedDouble = 000123.456

// 読みやすさのために、"_"を間に挟んでも良い
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1

数値型変換

整数変換

整数の場合は、自動的に型変換が行われる。

// Error
UInt8 = -1 
Int8 = Int8.max + 1

整数と浮動小数点の変換

// 整数型と浮動小数点型の間の変換は明示的に行う必要がある。
let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine

// 浮動小数点から整数への変換も明示的に行う必要がある。
let integerPi = Int(pi)
// この場合、浮動小数点値は常に切り捨てられる。
// 例えば、4.75 >>> 4、  -3.9 >>> -3 になる。

タイプエイリアス

typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0

Bool

trueまたはfalse

let i = 1
if i { } // これはコンパイルエラーになる。

タプル

複数の値を単一の複合値にグループ化したもの。タプル内の値は任意のタイプを持つことができる。関数の戻り値としてよく使われる。

let http404Error = (404, "Not Found")

// 分離
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
print("The status message is \(statusMessage)")

// アンダースコアの部分は無視される
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")

// ゼロから始まるインデックス番号を使用して、タプル内の個々の要素の値にアクセスできる。
print("The status code is \(http404Error.0)")
// Prints "The status code is 404"
print("The status message is \(http404Error.1)")
// Prints "The status message is Not Found"

// タプル内の個々の要素に名前を付けることができる。
let http200Status = (statusCode: 200, description: "OK")
print("The status code is \(http200Status.statusCode)")
// Prints "The status code is 200"
print("The status message is \(http200Status.description)")
// Prints "The status message is OK"

オプショナル

// nil になる可能性がある場合
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber is inferred to be of type "Int?", or "optional Int"

nil

値のない状態

var serverResponseCode: Int? = 404
serverResponseCode = nil

// デフォルト値を指定せずにオプションの変数を定義すると、nilとなる
var surveyAnswer: String?
// surveyAnswer は、nil で定義される

Swiftでは、nil はポインターではなく、特定の型の値がないことを示しています。オブジェクトタイプだけでなく、任意のタイプのオプションをに設定できます。

if文と強制アンラップ

ifステートメントを使用して、オプションとを比較することにより、オプションに値が含まれているかどうかを確認できますnil。この比較は、「等しい」演算子(==)または「等しくない」演算子(!=)を使用して実行します。

オプションに値がある場合、「等しくない」と見なされますnil

if convertedNumber != nil {
    // 値が存在している
    // !をつけることにより、値を強制アンラップできる。
    // 存在しない値にアクセスしようとすると、ランタイムエラーが発生する。
    // 強制アンラップする前に、nil ではないことを必ず確認すること。
    print("\(convertedNumber!).")
}

オプショナル バインディング

if文、while文で、値が含まれているかどうかを調べ、含まれている場合は、その値を一時的な定数または変数として使用できるようにする。

if let constantName = someOptional {
    statements
}

// 強制アンラップは、次のように書き換えることができる。
if let actualNumber = Int(possibleNumber) {
    // 値が存在している
    print("\(actualNumber)")
}

// Optional Bindingをコンマで区切って含めることができる。
if let firstNumber = Int("4"), let secondNumber = Int("42") {
     print("\(firstNumber) and \(secondNumber) ")
}

// これは、次の文と同等
if let firstNumber = Int("4") {
    if let secondNumber = Int("42") {
        print("\(firstNumber) < \(secondNumber)")
    }
}

guard 文は、別途説明。

暗黙的にアンラップされたオプショナル

// オプショナル
let possibleString: String? = "An optional string."
// ! による強制アンラップが必要
let forcedString: String = possibleString! // requires an exclamation point

// 暗黙的にアンラップされたオプショナル
let assumedString: String! = "An implicitly unwrapped optional string."
// ! による強制アンラップは必要ない。
let implicitString: String = assumedString

// 次の場合は、オプショナル
let optionalString = assumedString

// 暗黙的にアンラップされたオプショナル
if assumedString != nil {
    print(assumedString!)
}

// 暗黙的にアンラップされたオプショナル
if let definiteString = assumedString {
    print(definiteString)
}

エラー処理

エラー処理を使用して、プログラムの実行中に発生する可能性のあるエラー条件に対応します。

値の有無を使用して関数の成功または失敗を伝えることができるオプションとは対照的に、エラー処理では、失敗の根本的な原因を特定し、必要に応じて、プログラムの別の部分にエラーを伝播できます。 。

関数がエラー条件を検出すると、エラーをスローします。その関数の呼び出し元は、エラーをキャッチして適切に応答できます。

func canThrowAnError() throws {
    // エラーを throw することができる
}

do {
    try canThrowAnError()
    // エラーはthrowされなかった
} catch {
    // エラーがthrowされた
}

// Swiftは、エラーがcatch節で処理されるまで、自動的に現在のスコープから伝播します。

// さまざまなエラー条件に応答する方法
func makeASandwich() throws {
    // ...
}
do {
    try makeASandwich()
     eatASandwich()
} catch SandwichError.outOfCleanDishes {
    washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
    buyGroceries(ingredients)
}

アサーションとプリコンディション

アサーションプリコンディションは、実行時に行われるチェックです。これらを使用して、さらにコードを実行する前に、必須条件が満たされていることを確認します。アサーションまたはプリコンディションが true の場合、コードの実行は通常どおり続行されます。false の場合、コードの実行が終了し、アプリが終了します。

アサーションと前提条件の違い

アサーションはデバッグビルドでのみチェックで、開発中の間違いや誤った仮定を見つけるのに役立つ。
前提条件はデバッグビルドと本番ビルドの両方でチェックされ、本番環境で問題を検出するのに役立つ。

アサーションを使用したデバッグ

let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// このアサーションは、失敗する。

// アサーションメッセージを省略可能
assert(age >= 0)

// 既に条件をチェックしているので、無条件にアサーションに失敗する場合
if age >= 0 {
    print("You can ride the ferris wheel.")
} else {
    assertionFailure("A person's age can't be less than zero.")
}

プリコンディションの適用

// 実行を継続するためには必ず trueでなければならない
precondition(index > 0, "Index must be greater than zero.")