LoginSignup
3
3

More than 1 year has passed since last update.

[Swift] クロージャについて理解する

Last updated at Posted at 2022-10-20

Qiita初めての投稿になります。Swift初学者Yasuと申します。
Swift学習歴5ヶ月ほどの初学者ですが、
自身の学習アウトプット、整理も含め投稿始めました。
今回はクロージャについてまとめました。

クロージャとは

処理を定義しつつ、周りのデータを取り込んで、後から実行できるもの。

みたいなイメージです。

初めはあまりピンとこないかと思いますが、
この記事を読み終える頃にイメージが湧いてると嬉しいです。

クロージャの定義方法

closure.swift
    { (仮引数: ,...) ->   in
        ...
    }

//関数の場合と比較
    func 関数名(仮引数: ,...) ->  {
        ...
    }

こうして見て見ると、クロージャと関数の共通点がいくつかあるのが分かりますね。
クロージャと関数を見極めるポイントとしては、

  • funcキーワードの有無
  • 関数名の有無
  • inの有無
  • {}の位置

クロージャを実行してみる

//cにクロージャのインスタンスが代入される
let c = { () -> Void in print("Hello!") }         

c() //Hello!

//関数の場合と比較
func c() {
    print("Hello!")
}
c()  //Hello!

関数を作って動かす 関数の場合は関数名()で動かすが、
クロージャの場合は、変数にクロージャのインスタンスを格納し、格納されている変数名・定数名()で動かせる

  • クロージャを即実行したい時

クロージャを定数や変数に代入せずに、即実行したい場合は、クロージャ式の後ろに()をつける

{ () -> Void in print("Hello!") }()

//Hello!  

値の渡し方、返し方

注意すべきポイントとしては、

  • 文が複数で値を返したい場合は、returnが必須となっているため注意する

  • 関数の場合だと、calculate(a: 10, b: 20) とラベルをつけて引数を渡すが、
    クロージャの場合は、calculate(10,20) とラベルなしで引数を渡す

let calculate = { (a: Int, b: Int) -> Int in
    let aa = a * 2
    let bb = b * 3
    return aa + bb  //文が複数(let定義が2つあるため)で値を返したい場合は、returnが必須
}

let result = calculate(10,20)  //クロージャはラベルなしで引数を渡す

returnを省略できる場合

return文が一つだけである場合は、値として返したい式だけを返してもよい

let add = { (a: Int, b: Int) -> Int in
    a + b   //returnを省略できる
}

返り値の型推論(返り値の型を省略できる)

文が1つしか存在しない場合に、返り値の型が推論されるため省略可

let c1 = { () -> () in print("Swift") }

let c2 = { () -> Void in print("Swift") } //Voidと()は同じ

let c3 = { () in print("Swift") } //返り値の型を省略 中身が返り値なしのため

let c4 = { print("Swift") }  //引数を省略

//Int型の場合
let value1: Int = { () -> Int in 100}()

let value2 = { 100 }()

クロージャを定数、変数に代入してみる

クロージャは同じ型の定数、変数に代入することができます。

let add1 = { (a: Int, b:Int) -> Int in
    return a+ b
}

let add2: (Int,Int) -> Int = add1  //add2にadd1のクロージャを代入

print(add2(10,20)) 

関数を代入するパターン

  • 関数の区別と指定

func f1(value: Int) -> {}

let f2 = f1(name: ) //引数ラベルはあってもなくても良い
  • 引数ラベルまで書くと指定できるパターン

この場合だと、代入する関数の引数ラベルを指定してあげないと、
どちらの関数か区別することができませんね。

func f1(name: String) -> {}

func f1(number: Int) -> {}

let c1 = f1(name: ) 

let c2 = f1(number: ) 
  • 引数ラベルでも区別できないパターン

この場合だと、引数ラベルの指定もできません。
引数ラベルの指定が無理なら、代入先の変数、定数内で型を指定をして区別します。

func f1(_ name: String) -> {}

func f1(_ number: Int) -> {}

let c1: (String) -> String = f1

let c2: (Int) -> Int = f1

クロージャのオプショナル

クロージャがないことをオプショナル型で表したい時

  • 間違った書き方

これだと、Intを引数で二つ受けとって、Int型のオプショナルを返すと言う文になってしまいます。

var c1: (Int, Int) -> Int?
  • 正しい書き方

Intを引数で二つ受けとって、Int型のオプショナルを返す関数を返すオプショナル型と言う文になります・
変数c2にはクロージャを代入することもできるし、nilを代入することもできます

var c2: ((Int, Int) -> Int)?

クロージャの配列

Intを引数として2つ受け取り、Int型の値を一つ返すという処理自体を[]で囲んであげることで、
クロージャを配列として扱えます。

var c1: [(Int, Int) -> Int] 
  • クロージャの型の別名を定義

毎回配列の型を定義するのは面倒、、と言うときは別名を定義し、その中に格納してあげることもできます。
(Int, Int) -> Int CalculationCloureに格納し、それを配列として扱ってあげることで、
var c1: [(Int, Int) -> Int]と同じ処理

typealias CalculationCloure = (Int, Int) -> Int

let c1: [CalculationCloure]   //var c1: [(Int, Int) -> Int] と同じ定義内容
3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3