@autoclosure @MainActor @Sendable @escaping () -> Void

0

@escaping

Swift에서 @escaping은 클로저가 함수나 메서드의 실행이 종료된 후에도 호출될 수 있음을 나타냅니다.
기본적으로 클로저는 함수 내에서만 유효하고, 함수가 종료되면 클로저도 메모리에서 해제됩니다.
그러나, 클로저가 함수 밖에서 나중에 호출되거나 저장하려면 @escaping으로 명시해야 합니다.

import Foundation

class MyClass {
    var storedClosure: () -> () = {}
    
    func store(closure: () -> ()) {
        storedClosure = closure
    }
}

func async(closure: () -> ()) {
    DispatchQueue.main.async {
        closure()
    }
}

@escaping을 사용할 때 주의사항

@escaping을 사용하면 reference counting을 이용하여 클로저가 함수의 스택 프레임에 벗어나도 클로저를 메모리에 유지합니다.
때문에 [weak self] 와 같은 방법을 이용하여 메모리 관리에 신경을 써야 합니다.

@Sendable

여러 thread에서 동시에 함수나 클로저를 실행하는 경우 race condition이 발생할 수 있습니다.
@Sendable은 다른 thread에서 실행될 때에도 안전하게 동기화될 수 있도록 보장하는 attribute입니다.

import Foundation

actor Counter {
    var count = 0
}

let counter = Counter()

let closure: @Sendable () -> Void = {
    counter.count += 1
}

@Sendable의 규칙

  1. 클로저에서 캡쳐한 변수, 상수를 thread-safe하게 다루어야 합니다. 클로저 내에서 mutating한 변수를 캡쳐하려고 할 경우 컴파일 에러가 발생합니다.
  2. 값 타입을 주로 사용합니다.
  3. non-Sendable 타입을 캡처하지 않습니다.

@MainActor

iOS에서 UI 업데이트는 반드시 main thread에서 실행되도록 제한됩니다.
UI 업데이트는 최적화를 위해 thread safe하게 설계되지 않았고, 업데이트에 순서가 정해져 있기 때문입니다.

import Foundation

@MainActor func mainActorFunction() {
    print("run on main")
}

func runClosure(_ closure: @escaping @Sendable () -> Void) {
    Task {
        closure()
    }
}

runClosure {
    mainActorFunction()
}

이를 해결하려면 아래와 같이 @MainActor를 closure에 붙여주면 됩니다.

func runClosure(_ closure: @escaping @Sendable @MainActor () -> Void) {
    Task {
        await closure()
    }
}

@autoclosure

클로저를 명시적으로 작성하지 않고도 자동으로 변환해주는 속성입니다.
쉽게 말해 클로저를 인자로 전달할 때 { } 를 사용하지 않아도 동작할 수 있도록 해줍니다.

import Foundation

func bool(_ closure: () -> Bool) {
    closure() ? print("true") : print("false")
}

bool { 2 > 1 }

func bool2(_ closure: @autoclosure () -> Bool) {
    closure() ? print("true") : print("false")
}

bool2 (2 > 1)

결론

이제 우리는 아래와 같은 함수를 이해할 수 있습니다. 아마도

func nowWeCanUnderstand(_ closure: @autoclosure @MainActor @Sendable @escaping () -> Void) {
    Task {
        await closure()
    }
}

nowWeCanUnderstand(print("hello world"))
profile
https://github.com/sustainable-git

0개의 댓글

관련 채용 정보