ぜのぜ

しりとりしようぜのぜのぜのぜ

25日目 関数をエスケープするクロージャ

日記

最近,両親が野菜やら果物やらを作るのにはまっているらしく,結構な量が贈られてくる.美味しいし助かるんだがいかんせん量が多いので食べきれない.それで昨日はお腹が空いていなかったのもあってビール片手に漬物や佃煮を作った.今日はそれをつまみに一杯やる予定.このブログで今日やることは全部終わりなので飲むぞ.

今日書いたコード

var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}
func someFunctionWithNonescapingClosure(closure: () -> Void) {
    closure()
}

print("SomeClass")
class SomeClass {
    var x = 10
    func doSomething() {
        print(x) // => 10
        someFunctionWithEscapingClosure { self.x = 100 } // (1)
        someFunctionWithEscapingClosure { [self] in x = 200 } // (2)
        completionHandlers.forEach { closure in
            closure()
            print(x)
        }
        // => 100
        // => 200
    }
}
SomeClass().doSomething()

print("SomeStruct")
struct SomeStruct {
    var x = 10
    mutating func doSomething() {
        print(x) // => 10
        someFunctionWithNonescapingClosure { x = 200 }  // (3)
        print(x) // => 200
        // someFunctionWithEscapingClosure { x = 100 }  // (4)
    }
}
var some = SomeStruct()
some.doSomething()

初めて知ったこと

関数をエスケープするクロージャの中でプロパティを触るとき,その関数をクラスのインスタンスが呼んでいるのであれば明示的にselfをつける(1)か,selfをキャプチャする(2)必要がある.一方で,structやenumインスタンスが呼んでいるのであればselfを付ける必要はない.この理由については以下のように書いてある.

Capturing self in an escaping closure makes it easy to accidentally create a strong reference cycle. ...... Writing self explicitly lets you express your intent, and reminds you to confirm that there isn’t a reference cycle. https://docs.swift.org/swift-book/LanguageGuide/Closures.htm