⎮ 주의
이 오류는 Swift5에서는 뜨지 않고, Swift6에서 뜬다.
(서치해보니, @Sendable 이라는 개념이 Swift 5.5부터 나온 것 같다)
아마 Swift 5에서는 이 오류가 경고정도로만 나오거나, 아예 나오지 않을 수 있다.
⎮ 어쩌다가 이 오류를 만나게 되었나?
클로저를 공부하다가, 클로저의 특성이 일급시민 객체의 특성을 따른다는 것을 공부하게 되었다.
String Type을 가진 변수에 다른 String를 대입할 수 있는 것처럼, 함수에도 클로저를 대입하고자 했다.
// string이 Type이기 때문에 변수에 담을 수 있고 동일한 타입의 값으로 변경이 가능하다
var stringNumber = "123"
stringNumber = "456"
// 마찬가지로 int도 Type이기 때문에 변수에 담을 수 있고 동일한 타입의 값으로 변경이 가능하다
var number = 123
number = 456
// 함수도 () -> () Type이므로 담을 수 있고,
var myFunction = printMyName
func printMyName() {
print("당신의 이름은 홍길동입니다.")
}
myFunction()
// closure도 () -> () Type이기 때문에 담을 수 있음
var yourFunction = {
print("당신의 이름은 고길동입니다")
}
// 같은 Type이니까 담아주려고 했는데, 에러가 발생했다.
myFunction = yourFunction // 에러 발생
myFunction = yourFunction을 했더니 하단의 에러가 발생했다.
Converting non-sendable function value to '@Sendable () -> ()' may introduce data races
아니 분명히 myFunction도 () -> () Type이고, yourFunction도 () -> () Type인데 왜 이러는걸까?
⎮ Swift의 Type 추론
에러 메시지를 다시 한번 보자
Converting non-sendable function value to '@Sendable () -> ()' may introduce data races
전송이 불가능한 함수 값을 '@Sendable () -> ()'로 변환하면 데이터의 꼬임(data race)이 일어날 수 있습니다.
엥? 둘다 () -> () Type 아니었나?
@Sendable은 뭐고 갑자기 어디서 튀어나왔을까?
⎮ Sendable 쉽게 이해하기
위 오류는 비동기 환경(Swift concurrency)에서 스레드 안전성(thread safety) 문제를 막기 위해 Swift가 알려주는 경고문이다.
(아마 더욱 안전한 환경을 위해 Swift가 새롭게 도입한 시스템 같음)
Swift에서는 @Sendable이라는 특성을 통해, 클로저가 스레드간 안전하게 전달될 수 있는지를 검사하는데,
비동기 작업이나 Task 안에서 사용하는 클로저는 Sendable이어야 함.
클로저는 기본적으로 non-Sendable인데,
var count = 0
let closure = {
count += 1 // 주변 변수 캡처!
}
위의 코드에서처럼 closure는 변수(mutable한 값)도 캡처할 수 있음
따라서, 값이 변할 가능성이 있기 때문에 기본적으로는 non-Sendable임.
위에서 선언한 함수와 클로저 둘다 () -> () Type은 맞지만,
var myFunction = printMyName
func printMyName() {
print("당신의 이름은 홍길동입니다.")
}
컴파일러가 타입을 추론할 때 myFunction의 타입을 @Sendable 함수로 추론함
var yourFunction = {
print("당신의 이름은 고길동입니다")
}
myFunction = yourFunction
후에 대입한 클로저가 일반 클로저(non-sendable)이라서
"전송이 불가능한 함수 값을 '@Sendable () -> ()'로 변환하면 데이터의 충돌이 일어날 수 있습니다."라고 알려준 것이다.
해결 방법으로는,
1. 함수의 타입을 명확히 () -> Void라고 명시해주거나
// 타입을 명확히 () -> Void라고 명시해주는 방법
var myFunction: () -> Void = printMyName
2. 반대로 클로저에 @Sendable을 붙여주는 방법이 있다.
* 단 이때는 클로저 안에서 캡처하는 값들이 반드시 Sendable(변하지 않는 값)이어야 함
// 클로저를 @Sendable로 명시하는 방법
var yourFunction: @Sendable () -> Void = {
print("당신의 이름은 고길동입니다")
}
⎮ Sendable 어렵게 이해하기(with WWDC 2022)
https://developer.apple.com/videos/play/wwdc2022/110351/
Eliminate data races using Swift Concurrency - WWDC22 - Videos - Apple Developer
Join us as we explore one of the core concepts in Swift concurrency: isolation of tasks and actors. We'll take you through Swift's...
developer.apple.com
( 어떻게든 이해해보고 싶어서 WWDC를 보면서 Keynote로 그려가며 이해하고자 했으나...... 어느정도만 이해하고 실패... )
이걸 이해하려면, 비동기 작업을 해보고나서 다시 들여다봐야 비로소 이해할 수 있을 것 같다....
⎮ @Sendable 공식문서
https://developer.apple.com/documentation/swift/sendable
Sendable | Apple Developer Documentation
A thread-safe type whose values can be shared across arbitrary concurrent contexts without introducing a risk of data races.
developer.apple.com
Sendable을 공식문서에서 찾아보면 다음과 같이 나와있다.
A thread-safe type whose values can be shared across arbitrary concurrent contexts without introducing a risk of data races.
("값이 어떤 동시 실행 환경에서도 안전하게 공유될 수 있도록 설계된, 스레드-안전한 타입이다. 이 타입은 데이터 레이스(꼬임)의 위험 없이 사용할 수 있다.")
즉, 이 말은 클로저가 스레드 간 안전하게 전달될 수 있는지를 확인하는 것이다.
Sendable 타입의 값은 공유되는 mutable한 상태의 값을 가지지 않거나 또는 해당 상태를 락(lock)으로 보호하거나
특정 액터(actor)에서만 접근하도록 강제할 수 있어야 한다.
Sendable Type의 값은 하나의 동시성 영역에서 다른 영역으로 안전하게 전달할 수 있다.
예를 들어, 액터의 메서드를 호출할 때 Sendable 값을 인자로 넘겨줄 수 있다.
아래의 항목들은 모두 Sendable로 표시될 수 있다.
- 값 타입(enum, struct 등)
- 변경 가능한 저장 속성이 없는 참조 타입(let으로 선언된 class)
- 내부적으로 상태 접근을 관리하는 참조 타입(lock을 쓰거나, actor 안에서만 상태를 변경하도록 만든 class)
- 함수와 클로저(@Sendable로 표시한 경우에만)
이 프로토콜은 요구하는 메서드나 프로퍼티는 없지만,
컴파일 시점에 검사되는 의미상의 규칙(semantic requirements)은 존재한다.
이러한 요구사항들은 아래 섹션에 나열되어 있다.
Sendable을 준수하려면, 그 선언은 반드시 해당 타입을 정의한 파일 안에서 해야 한다.
컴파일러의 검사 없이 Sendable을 선언하려면,
@unchecked Sendable을 사용하면 된다. (단, 타입이 안전한지의 여부는 개발자 본인의 책임이다.)
예를 들어, 락(Lock)이나 큐(Queue)를 사용해 상태 접근을 보호해야 한다.
@unchecked Sendable을 사용하면 Sendable은 반드시 같은 파일에서 선언해야한다는 규칙이 적용되지 않는다.
'Swift > 오류 개발자' 카테고리의 다른 글
Swift | View 전환 시 기존 View가 살아 있는 문제 해결하기 (0) | 2025.04.20 |
---|---|
Swift | Custom Font 적용이 안되는 오류 해결하기 (0) | 2025.04.07 |
Swift | presentingViewController.dismiss 오류(feat. 휴먼 에러) (0) | 2025.03.26 |
Swift | SnapKit no such file or directory 오류 (2) | 2025.03.18 |
Swift | TableView에서 특정 영역만 선택이 가능하도록 만들기 (0) | 2025.03.09 |