수색…


통사론

  • var closureVar : (<parameters>) -> (<returnType>) // 변수 또는 속성 유형으로
  • typeAlias ​​ClosureType = (<매개 변수>) -> (<returnType>)
  • <statements>에서 <[<captureList>] (<parameters>) <throws-ness> -> <returnType> // 클로저 구문 완성

비고

스위프트 클로저에 대한 자세한 내용은 Apple의 설명서를 참조하십시오.

클로저 기본 사항

클로저 ( 블록 또는 람다 라고도 함)는 프로그램 내에서 저장되고 전달 될 수있는 코드 조각입니다.

let sayHi = { print("Hello") }
// The type of sayHi is "() -> ()", aka "() -> Void"

sayHi()  // prints "Hello"

다른 함수와 마찬가지로 클로저는 인수를 받아들이고 결과를 반환하거나 오류를 throw 할 수 있습니다 .

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 본체 } .

이 부분들 중 많은 부분을 생략 할 수 있으므로 간단한 closure를 작성하는 몇 가지 방법이 있습니다.

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. 사용 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 주 :

스위프트 3에서는 더 이상 블록을 @noescape로 표시하지 않습니다. 이제 블록이 기본적으로 이스케이프 되지 않습니다 . 스위프트 3에서는 클로저를 비 이스케이프로 표시하는 대신 이스케이프 클로저 인 함수 매개 변수를 "@escaping"키워드를 사용하여 이스케이프 처리로 표시합니다.


throwsrethrows

클로저는 다른 함수와 마찬가지로 오류를 발생 시킬 수 있습니다 .

func executeNowOrIgnoreError(block: () throws -> Void) {
    do {
        try block()
    } catch {
        print("error: \(error)")
    }
}

함수는 물론 호출자에게 오류를 전달할 수 있습니다.

func executeNowOrThrow(block: () throws -> Void) throws {
    try block()
}

그러나 전달 된 블록이 throw 되지 않으면 호출자는 여전히 throwing 함수로 멈춘다.

// It's annoying that this requires "try", because "print()" can't throw!
try executeNowOrThrow { print("Just printing, no errors here!") }

솔루션은 rethrows 이며, closure 매개 변수가 throw하는 경우 에만 함수가 throw 할 수 있음을 나타냅니다.

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() 포함하여 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"

Closure의 캡쳐리스트 는 weak 또는 unowned reference를 지정하는데 사용될 수있다 :

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)")
        }
    }
}

비동기 코딩을위한 클로저 사용하기

클로저는 종종 비동기 작업 (예 : 웹 사이트에서 데이터 가져 오기)에 사용됩니다.

3.0
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 응용 프로그램의 주 스레드에서 호출 될 경우 인터페이스가 고정되지 않습니다).

3.0
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

그런 다음 typealias를 사용하여 함수를 정의 할 수 있습니다.

public func closureFunction(closure: ClosureType) {
    let z = closure(1, 2)
}
    
closureFunction() { (x: Int, y: Int) -> Int in return x + y }


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow