Swift Language
閉鎖
サーチ…
構文
- var closureVar:(<parameters>) - >(<returnType>)//変数またはプロパティの型として
- タイプアライアンスClosureType =(<parameters>) - >(<returnType>)
- <statement>内の<[<captureList>](<parameters>)<throws-ness> - > <returnType> //完全なクロージャの構文
備考
Swift closureの詳細については、 Appleのマニュアルを参照してください。
クロージャの基礎
クロージャ (また、 ブロックまたはラムダとして知られている)は、プログラム内に格納されていると周りに渡すことができるコードの断片です。
let sayHi = { print("Hello") }
// The type of sayHi is "() -> ()", aka "() -> Void"
sayHi() // prints "Hello"
他の関数と同様に、クロージャは引数を受け入れて結果を返したり、 エラーを投げたりすることができます:
let addInts = { (x: Int, y: Int) -> Int in
return x + y
}
// The type of addInts is "(Int, Int) -> Int"
let result = addInts(1, 2) // result is 3
let divideInts = { (x: Int, y: Int) throws -> Int in
if y == 0 {
throw MyErrors.DivisionByZero
}
return x / y
}
// The type of divideInts is "(Int, Int) throws -> Int"
クロージャは、スコープから値を取得できます。
// This function returns another function which returns an integer
func makeProducer(x: Int) -> (() -> Int) {
let closure = { x } // x is captured by the closure
return closure
}
// These two function calls use the exact same code,
// but each closure has captured different values.
let three = makeProducer(3)
let four = makeProducer(4)
three() // returns 3
four() // returns 4
クロージャは関数に直接渡すことができます:
let squares = (1...10).map({ $0 * $0 }) // returns [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
let squares = (1...10).map { $0 * $0 }
NSURLSession.sharedSession().dataTaskWithURL(myURL,
completionHandler: { (data: NSData?, response: NSURLResponse?, error: NSError?) in
if let data = data {
print("Request succeeded, data: \(data)")
} else {
print("Request failed: \(error)")
}
}).resume()
構文のバリエーション
基本的なクロージャ構文は次のとおりです。
{
[
キャプチャリスト]
(
パラメータ)
ネススロー->
戻り型in
本体}
。
これらの部分の多くは省略することができるので、単純なクロージャを書くにはいくつかの同等の方法があります:
let addOne = { [] (x: Int) -> Int in return x + 1 }
let addOne = { [] (x: Int) -> Int in x + 1 }
let addOne = { (x: Int) -> Int in x + 1 }
let addOne = { x -> Int in x + 1 }
let addOne = { x in x + 1 }
let addOne = { $0 + 1 }
let addOneOrThrow = { [] (x: Int) throws -> Int in return x + 1 }
let addOneOrThrow = { [] (x: Int) throws -> Int in x + 1 }
let addOneOrThrow = { (x: Int) throws -> Int in x + 1 }
let addOneOrThrow = { x throws -> Int in x + 1 }
let addOneOrThrow = { x throws in x + 1 }
- キャプチャリストは、空の場合は省略できます。
- 型の推論が可能な場合、パラメータに型注釈は必要ありません。
- 戻り型を推論できる場合は、指定する必要はありません。
- パラメータの名前を付ける必要はありません。代わりに
$0
、$1
、$2
などで参照できます。 - クロージャーに値が返される単一の式が含まれている場合、
return
キーワードは省略できます。 - クロージャがエラーをスローすると推測された場合、スロークロージャが必要なコンテキストで記述されている場合、またはエラーを
throws
していない場合はthrows
を省略できます。
// The closure's type is unknown, so we have to specify the type of x and y.
// The output type is inferred to be Int, because the + operator for Ints returns Int.
let addInts = { (x: Int, y: Int) in x + y }
// The closure's type is specified, so we can omit the parameters' type annotations.
let addInts: (Int, Int) -> Int = { x, y in x + y }
let addInts: (Int, Int) -> Int = { $0 + $1 }
クロージャを関数に渡す
関数は、クロージャ(または他の関数)をパラメータとして受け入れることができます:
func foo(value: Double, block: () -> Void) { ... }
func foo(value: Double, block: Int -> Int) { ... }
func foo(value: Double, block: (Int, Int) -> String) { ... }
末尾クロージャの構文
関数の最後のパラメータがクロージャの場合、関数の呼び出しの後にクロージャ中括弧{
/ }
が記述されることがあります。
foo(3.5, block: { print("Hello") })
foo(3.5) { print("Hello") }
dispatch_async(dispatch_get_main_queue(), {
print("Hello from the main queue")
})
dispatch_async(dispatch_get_main_queue()) {
print("Hello from the main queue")
}
関数の唯一の引数がクロージャである場合、末尾のクロージャ構文でそれを呼び出すときに、かっこ()
ペアを省略することもできます。
func bar(block: () -> Void) { ... }
bar() { print("Hello") }
bar { print("Hello") }
@noescape
パラメータ
@noescape
マークされた閉包パラメータは、関数呼び出しが戻る前に実行されることが保証されていますself.
クロージャ本体の内側には必要ありません。
func executeNow(@noescape block: () -> Void) {
// Since `block` is @noescape, it's illegal to store it to an external variable.
// We can only call it right here.
block()
}
func executeLater(block: () -> Void) {
dispatch_async(dispatch_get_main_queue()) {
// Some time in the future...
block()
}
}
class MyClass {
var x = 0
func showExamples() {
// error: reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit
executeLater { x = 1 }
executeLater { self.x = 2 } // ok, the closure explicitly captures self
// Here "self." is not required, because executeNow() takes a @noescape block.
executeNow { x = 3 }
// Again, self. is not required, because map() uses @noescape.
[1, 2, 3].map { $0 + x }
}
}
スウィフト3ノート:
Swift 3では、ブロックを@noescapeとマークすることはもうありません。ブロックはデフォルトではエスケープされません 。 Swift 3では、クロージャを非エスケープとしてマークするのではなく、エスケープクロージャである関数パラメータを「@エスケープ」キーワードを使用してエスケープするとマークします。
throws
rethrows
クロージャは、他の関数と同様に、 エラーをスローする可能性があります。
func executeNowOrIgnoreError(block: () throws -> Void) {
do {
try block()
} catch {
print("error: \(error)")
}
}
関数は、もちろん、その呼び出し元にエラーを渡すことができます:
func executeNowOrThrow(block: () throws -> Void) throws {
try block()
}
ただし、渡されたブロックがスローされない場合、呼び出し元はスロー関数を使用しています。
// It's annoying that this requires "try", because "print()" can't throw!
try executeNowOrThrow { print("Just printing, no errors here!") }
解決策はrethrows
であり、クロージャパラメータがスローする場合にのみ関数がスローできることを示します 。
func executeNowOrRethrow(block: () throws -> Void) rethrows {
try block()
}
// "try" is not required here, because the block can't throw an error.
executeNowOrRethrow { print("No errors are thrown from this closure") }
// This block can throw an error, so "try" is required.
try executeNowOrRethrow { throw MyError.Example }
多くの標準ライブラリ関数は、 map()
、 filter()
、 indexOf()
などのindexOf()
を使用しrethrows
。
キャプチャ、強/弱参照、保持サイクル
class MyClass {
func sayHi() { print("Hello") }
deinit { print("Goodbye") }
}
クロージャが参照型(クラスインスタンス)を取得するとき、クロージャはデフォルトで強い参照を保持します:
let closure: () -> Void
do {
let obj = MyClass()
// Captures a strong reference to `obj`: the object will be kept alive
// as long as the closure itself is alive.
closure = { obj.sayHi() }
closure() // The object is still alive; prints "Hello"
} // obj goes out of scope
closure() // The object is still alive; prints "Hello"
クロージャのキャプチャリストを使用して、弱いまたは所有されていない参照を指定できます。
let closure: () -> Void
do {
let obj = MyClass()
// Captures a weak reference to `obj`: the closure will not keep the object alive;
// the object becomes optional inside the closure.
closure = { [weak obj] in obj?.sayHi() }
closure() // The object is still alive; prints "Hello"
} // obj goes out of scope and is deallocated; prints "Goodbye"
closure() // `obj` is nil from inside the closure; this does not print anything.
let closure: () -> Void
do {
let obj = MyClass()
// Captures an unowned reference to `obj`: the closure will not keep the object alive;
// the object is always assumed to be accessible while the closure is alive.
closure = { [unowned obj] in obj.sayHi() }
closure() // The object is still alive; prints "Hello"
} // obj goes out of scope and is deallocated; prints "Goodbye"
closure() // crash! obj is being accessed after it's deallocated.
詳細については、 メモリ管理のトピックとSwiftプログラミング言語の自動参照カウントのセクションを参照してください。
保持サイクル
オブジェクトがクロージャー上に保持されている場合、オブジェクトへの強い参照も保持されますが、これは保持サイクルです。サイクルが壊れていない限り、オブジェクトとクロージャを格納しているメモリはリークします(再利用されることはありません)。
class Game {
var score = 0
let controller: GCController
init(controller: GCController) {
self.controller = controller
// BAD: the block captures self strongly, but self holds the controller
// (and thus the block) strongly, which is a cycle.
self.controller.controllerPausedHandler = {
let curScore = self.score
print("Pause button pressed; current score: \(curScore)")
}
// SOLUTION: use `weak self` to break the cycle.
self.controller.controllerPausedHandler = { [weak self] in
guard let strongSelf = self else { return }
let curScore = strongSelf.score
print("Pause button pressed; current score: \(curScore)")
}
}
}
非同期コーディングのクロージャの使用
クロージャは、Webサイトからデータを取得する場合など、非同期タスクによく使用されます。
func getData(urlString: String, callback: (result: NSData?) -> Void) {
// Turn the URL string into an NSURLRequest.
guard let url = NSURL(string: urlString) else { return }
let request = NSURLRequest(URL: url)
// Asynchronously fetch data from the given URL.
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {(data: NSData?, response: NSURLResponse?, error: NSError?) in
// We now have the NSData response from the website.
// We can get it "out" of the function by using the callback
// that was passed to this function as a parameter.
callback(result: data)
}
task.resume()
}
この関数は非同期なので、呼び出されているスレッドをブロックしません(GUIアプリケーションのメインスレッドで呼び出された場合はインターフェイスをフリーズしません)。
print("1. Going to call getData")
getData("http://www.example.com") {(result: NSData?) -> Void in
// Called when the data from http://www.example.com has been fetched.
print("2. Fetched data")
}
print("3. Called getData")
タスクは非同期であるため、出力は通常次のようになります。
"1. Going to call getData"
"3. Called getData"
"2. Fetched data"
クロージャの内部のコードprint("2. Fetched data")
は、URLのデータがフェッチされるまで呼び出されません。
クロージャーとタイプエイリアス
クロージャーは、 typealias
で定義することができます。複数の場所で同じクロージャ署名が使用されている場合は、便利な型プレースホルダが用意されています。たとえば、一般的なネットワーク要求コールバックやユーザーインターフェイスイベントハンドラは、型名を使用して名前を付けることができます。
public typealias ClosureType = (x: Int, y: Int) -> Int
次に、タイプアリアスを使用して関数を定義することができます。
public func closureFunction(closure: ClosureType) {
let z = closure(1, 2)
}
closureFunction() { (x: Int, y: Int) -> Int in return x + y }