Swift :: @noescape explained

#@noescape

@noescape는 함수 파라미터로 클로져를 선언할 때 해당 클로저내의 모든 정보가 외부로 나갈 수 없음을 명시한다. 표현이 좀 애매한데, @noescape로 선언된 클로져는 다음의 동작만이 가능하다.

  1. 클로저가 인자로 전달된 함수 내에서 호출 가능
  2. 함수 내에서 다른 함수(이 때 이 다른 함수도 클로져를 @noescape로 선언한 경우에만)로 전달하여 호출할 수 있다.
  3. 다른 함수나 클로저에서 역시 @noescape로 선언된 경우에 캡쳐

다음의 경우에는 쓸 수 없게 된다.

  1. 다른 지역변수에 대입이 불가하다. 이는 클로져 내의 캡쳐된 모든 변수에 대해서 추가적인 강한 참조를 더할 수 없다는 뜻이다.
  2. 같은 이치로 클로저를 받은 함수 내부에서 다시 다른 함수로 해당 클로저를 전달하려고 할 때, 그 함수의 파라미터에 @noescape가 선언되지 않은 함수로는 전달이 불가능하다.
  3. 2에 의해서 병렬처리 함수로 전달할 수 없다.

이는 함수를 호출하는, 즉 클로져를 제공하는 입장에서보면 클로져가 캡쳐하는 변수들의 라이프사이클에 대해서 더 이상 고민하지 않아도 된다는 뜻이다.

func doIt(code: () -> ()) {
    code()
}

class Foo {
    var i = 0
    func some() {
        doIt {
            println(self.i)
        }
    }
}

let foo = Foo()
foo.some()

위 코드에서 보면 클래스 Foo의 somedoIt으로 클로저를 전달하는데, 이 때 클로져는 self를 캡쳐하므로 참조 순환이 발생한다. (할 것이다 아마도)

이를 피하기 위해서는 [unowned self]를 추가하는 방법도 있지만,

func doIt(@noescape code: () -> ()) {
    code()
}
class Foo {
    var i = 0
    func some() {
        doIt {
            println(self.i)
        }
    }
}

doIt 함수가 @noescape를 명시하는 것도 방법이 될 수 있다. self 자체가 약한참조로 넘어가는 것이 보장되는 것은 클로져의 실행문맥이 self의 내부라는 의미가 되고 이는 클로져 내에서 굳이굳이 self를 쓰지 않아도 된다는 점이다.