Edited at

Swiftの @noescape をもっと使おう

More than 3 years have passed since last update.



(追記)

Swift 3 では @noescape の挙動がデフォルトになったため記述する必要がなくなりました。


よく知らなかったのですが、調べてみたら積極的に使っていこうという気になりました。


使用例

関数の引数で渡すクロージャにattributeとして付けます。

func action(@noescape closure: () -> ()) {

closure()
}

(Appleのドキュメント)


@noescape を付けたクロージャに保証されること


  • クロージャはどこへも保持されない

  • クロージャは非同期的に後から実行されることはない

つまり、

クロージャの生存期間が関数よりも短い

ことを保証することになります。

よって、クロージャを渡した先で


  • クロージャをインスタンス変数などとして保持

  • クロージャを非同期的に後で実行

などする場合にはクロージャの生存期間が関数よりも延びてしまうので @noescape を付けることができません

(StackOverflowの回答)


いいこと

クロージャ内で self. を書かなくてよくなります。


more importantly disables the “self.” requirement in closure arguments.


(リリースノートより)

以下のような理由からではないかと思われます。



  • self. の記述はクロージャの生存が終わるまではselfが開放されないことを明示するためのもの(たぶん)


  • @noescape が付いたクロージャは生存がすぐに終わる

  • クロージャの生存が終わってもselfは必ず存在する

  • つまり、 self. を書いてselfが開放されないことを明示する必要がない

self. を書く必要がないということは [weak self][unowned self] について考える必要がない、つまり selfについての循環参照を考える必要がなくなる ということです!


@noescape なしの例

func action(closure: () -> ()) {

closure()
}

class A {
var count = 0
func countAfterAction() {
action {
self.count++ // ここ
}
}
}


@noescape ありの例

func action(@noescape closure: () -> ()) {

closure()
}

class A {
var count = 0
func countAfterAction() {
action {
count++ // ここ
}
}
}


またパフォーマンスが良くなる効果もあるようです。


This enables some minor performance optimizations


(リリースノートより)


結論

@noescape を付けられる場合は @noescape を付けていきたい。


  • クロージャ内で self. を書く必要がなくなる

  • つまり、selfの循環参照の問題を気にしなくてよくなる

  • パフォーマンスが良くなる


(おまけ) noescapeの意味


  • 生き延びない (escapeしない) という意味みたいです。


This indicates that the parameter is only ever called (or passed as an @noescape parameter in a call), which means that it cannot outlive the lifetime of the call.


(リリースノートより)


環境

Apple Swift version 2.1 (swiftlang-700.1.101.6 clang-700.1.76)

Target: x86_64-apple-darwin14.5.0