[closures] Swift @autoclosure를 사용하는 방법

assertSwift에서을 쓸 때 첫 번째 값이 다음과 같이 입력되었음을 알았습니다.

@autoclosure() -> Bool

T통해 존재를 테스트하기 위해 일반 값 을 반환하는 오버로드 된 메소드 를 사용합니다 LogicValue protocol.

그러나 당면한 문제를 엄격히 고수하십시오. @autoclosure를 반환하는 것으로 보입니다 Bool.

매개 변수를 사용하지 않고 Bool을 반환하는 실제 클로저를 작성하면 작동하지 않습니다. 클로저를 호출하여 컴파일되도록하십시오.

assert({() -> Bool in return false}(), "No user has been set", file: __FILE__, line: __LINE__)

그러나 단순히 Bool을 전달하면 작동합니다.

assert(false, "No user has been set", file: __FILE__, line: __LINE__)

무슨 일이야? 무엇입니까 @autoclosure?

편집 : @auto_closure 이름이 변경되었습니다@autoclosure



답변

하나의 인수를 취하는 함수, 인수를 취하지 않는 간단한 클로저를 고려하십시오.

func f(pred: () -> Bool) {
    if pred() {
        print("It's true")
    }
}

이 함수를 호출하려면 클로저를 전달해야합니다

f(pred: {2 > 1})
// "It's true"

중괄호를 생략하면 표현식이 전달되며 오류입니다.

f(pred: 2 > 1)
// error: '>' produces 'Bool', not the expected contextual result type '() -> Bool'

@autoclosure표현식 주위에 자동 폐쇄를 작성합니다. 따라서 호출자가와 같은 표현식을 작성하면 에 전달 2 > 1되기 {2 > 1}전에 자동으로 클로저로 래핑됩니다 f. 따라서 이것을 함수에 적용하면 f:

func f(pred: @autoclosure () -> Bool) {
    if pred() {
        print("It's true")
    }
}

f(pred: 2 > 1)
// It's true

따라서 클로저로 감싸지 않고도 표현식으로 작동합니다.


답변

다음은 실용적인 예입니다. 내 print재정의 (Swift 3) :

func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "\n") {
    #if DEBUG
    Swift.print(item(), separator:separator, terminator: terminator)
    #endif
}

당신이 말할 때 print(myExpensiveFunction()), 내 print재정의 print는 Swift를 어둡게 하고 호출됩니다. myExpensiveFunction()따라서 폐쇄로 싸여져 평가되지 않습니다 . 우리는 릴리스 모드에 있다면, 그것은 것입니다 적이 있기 때문에, 평가되지 item()호출되지 않습니다. 따라서 print릴리스 모드에서 인수를 평가하지 않는 버전이 있습니다.


답변

문서의 auto_closure에 대한 설명 :

매개 변수 유형이 ()이고 표현식 유형을 리턴하는 함수 유형에 auto_closure 속성을 적용 할 수 있습니다 (유형 속성 참조). 자동 폐쇄 함수는 표현식 자체 대신 지정된 표현식에 대한 암시 적 폐쇄를 캡처합니다. 다음 예제는 매우 간단한 어설 션 함수를 정의 할 때 auto_closure 속성을 사용합니다.

그리고 여기 애플과 함께 사용하는 예제가 있습니다.

func simpleAssert(condition: @auto_closure () -> Bool, message: String) {
    if !condition() {
        println(message)
    }
}
let testNumber = 5
simpleAssert(testNumber % 2 == 0, "testNumber isn't an even number.")

기본적으로 의미하는 것은 부울 식을 클로저 대신 첫 번째 인수로 전달하고 자동으로 클로저를 생성한다는 것입니다. 따라서 부울식이 기 때문에 메서드에 false를 전달할 수 있지만 클로저를 전달할 수는 없습니다.


답변

이것은 https://airspeedvelocity.net/2014/06/28/extending-the-swift-language-is-cool-but-be-careful/ 의 유용한 사례를 보여줍니다@autoclosure

이제 첫 번째 매개 변수로 전달 된 조건식이 자동으로 클로저 표현식으로 랩핑되고 루프 주위에서 매번 호출 될 수 있습니다.

func until<L: LogicValue>(pred: @auto_closure ()->L, block: ()->()) {
    while !pred() {
        block()
    }
}

// doSomething until condition becomes true
until(condition) {
    doSomething()
}


답변

클로저 호출에서 중괄호를 제거하는 방법 일뿐입니다. 간단한 예는 다음과 같습니다.

    let nonAutoClosure = { (arg1: () -> Bool) -> Void in }
    let non = nonAutoClosure( { 2 > 1} )

    let autoClosure = { (arg1: @autoclosure () -> Bool) -> Void in }
    var auto = autoClosure( 2 > 1 ) // notice curly braces omitted


답변

@autoclosure일반 closure은 원시 함수 를 승인하는 동안 요리 된 함수 (또는 리턴 된 유형)를 승인하는 함수 매개 변수입니다.

  • @autoclosure 인수 유형 매개 변수는 ‘()’이어야합니다.
    @autoclosure ()
  • @ autoclosure는 적절한 반환 유형 만 가진 모든 함수를 수락합니다.
  • 마감 결과는 수요에 따라 계산됩니다.

예를 보자

func testClosures() {

    //closures
    XCTAssertEqual("fooWithClosure0 foo0", fooWithClosure0(p: foo0))
    XCTAssertEqual("fooWithClosure1 foo1 1", fooWithClosure1(p: foo1))
    XCTAssertEqual("fooWithClosure2 foo2 3", fooWithClosure2(p: foo2))

    XCTAssertEqual("fooWithClosure2 foo2 3", fooWithClosure2(p: { (i1, i2) -> String in
        return "fooWithClosure2 " + "foo2 " + String(i1 + i2)
    }))

    //@autoclosure
    XCTAssertEqual("fooWithAutoClosure HelloWorld", fooWithAutoClosure(a: "HelloWorld"))

    XCTAssertEqual("fooWithAutoClosure foo0", fooWithAutoClosure(a: foo0()))
    XCTAssertEqual("fooWithAutoClosure foo1 1", fooWithAutoClosure(a: foo1(i1: 1)))
    XCTAssertEqual("fooWithAutoClosure foo2 3", fooWithAutoClosure(a: foo2(i1: 1, i2: 2)))

}

//functions block
func foo0() -> String {
    return "foo0"
}

func foo1(i1: Int) -> String {
    return "foo1 " + String(i1)
}

func foo2(i1: Int, i2: Int) -> String {
    return "foo2 " + String(i1 + i2)
}

//closures block
func fooWithClosure0(p: () -> String) -> String {
    return "fooWithClosure0 " + p()
}

func fooWithClosure1(p: (Int) -> String) -> String {
    return "fooWithClosure1 " + p(1)
}

func fooWithClosure2(p: (Int, Int) -> String) -> String {
    return "fooWithClosure2 " + p(1, 2)
}

//@autoclosure
func fooWithAutoClosure(a: @autoclosure () -> String) -> String {
    return "fooWithAutoClosure " + a()
}


답변