티스토리 뷰
✅ nonisolated 란?
nonisolated는 이 멤버는 actor의 격리(isolation)에서 빼달라 라는 선언입니다. Actor의 멤버는 기본적으로 격리되어 있으며 await로만 접근할 수 있는데, nonisolated를 붙이면 그 격리를 풀어 어디서나 동기적으로 호출할 수 있게 됩니다.
Actor는 자신의 가변상태(mutable state)를 데이터 레이스로부터 보호하기 위해 모든 멤버를 격리합니다. 그래서 Actor 밖에서 메서드나 프로퍼티에 접근하려고 하면 await가 필요하죠. 하지만 어떤 멤버는 Actor의 가변 상태를 전혀 건드리지 않았는데도 이 규칙에 묶여 불편할 때도 있습니다. 이때 우리가 사용하는게 nonisolated 입니다.
nonisolated 멤버는 Actor의 격리된 가변 상태에 접근할 수 없습니다. 접근 가능한건 불변(let) 프로퍼티, 다른 nonisolated 멤버, 그리고 자기 자신의 파라미터 뿐입니다. 이 제약 덕분에 await 없이 호출해도 data race가 발생하지 않는다는게 보장됩니다.
actor BankAccount {
let id: UUID // 불변(let)
var balance: Decimal // 격리된 가변 상태
nonisolated var label: String {
"Account \(id)" // OK: id는 let으로 불변임
}
// nonisolated var current: Decimal { balance } // 가변 상태이므로 nonisolated가 불가능
// 격리된 가변 상태(balance)에는 접근할 수 없음
}
let account = BankAccount(id: UUID(), balance: 100)
print(account.label) // await 없이 호출 가능
실무에서는 Hashable, Equatable, CustomStringConvertible, Identifiable, Codable 같은 프로토콜의 요구사항은 동기적이고 nonisolated 시그니처를 가집니다.
// Equatable
static func == (lhs: Self, rhs: Self) -> Bool // 동기
// Hashable
func hash(into hasher: inout Hasher) // 동기
// CustomStringConvertible
var description: String { get } // 동기
전부 await 없이 동기적으로 호출되도록 정의되어 있습니다. 여기서 충돌이 발생합니다.
Actor 멤버는 기본적으로 격리되며 호출하기 위해서는 await를 필요로 합니다. 하지만 위 프로토콜들은 동기 시그니처를 띄고 있기 때문에 await 를 붙일 수 없죠.
Actor가 이런 프로토콜을 준수하려면 해당 멤버를 nonisolated로 만들필요가 있습니다.
actor Player: CustomStringConvertible, Hashable {
let id: UUID
var score: Int
nonisolated var description: String { "Player(\(id))" }
nonisolated func hash(into hasher: inout Hasher) {
hasher.combine(id) // 불변 id만 사용
}
nonisolated static func == (lhs: Player, rhs: Player) -> Bool {
lhs.id == rhs.id
}
}
이때 주의해야 할 점은 nonisolated 멤버는 격리를 포기한 대신 가변 격리 상태(위의 코드에서는 var name)에 접근할 수 없습니다.
nonisolated var description: String {
"User(\(name))" // 에러: 격리된 var name에 동기 접근 불가
}
✅ nonisolated(unsafe)
nonisolated(unsafe)는 Swift 5.10 에서 추가된 escape hatch 입니다. 주로 전역변수나 static 저장 프로퍼티에서 Data race 검사를 끄고 동기화를 스스로 책임지겠다고 선언할 때 사용합니다.
nonisolated(unsafe) var legacyCache: [String: Data] = [:]
주로 레거시 코드를 마이그레이션 하거나 직렬 큐 등으로 접근을 직접 보장하는 경우에 쓰입니다.
이름 그대로 unsafe이며 안전성을 개발자가 직접 떠안는 방식이기 때문에 남용에 주의해야 하죠.
nonisolated(unsafe)는 일반 nonisolated과 다릅니다.
일반 은 컴파일러가 멤버의 안전함을 검증해주는 반명 unsafe는 컴파일러가 검사를 포기하죠.
전역변수나 static 저장 프로퍼티에서 주로 unsafe를 사용해야하는 상황이 발생합니다. 이 둘은 앱 어디서든, 어느 스레드에서든 접근할 수 있기 때문에 컴파일러 입장에선 막아버리기 때문이죠.
하지만 기존에 잘 돌아가던 수십만 줄의 코드가 Swift6으로 옮기는데 변수 하나 때문에 컴파일이 안되면 안되니까 unsafe로 막아두고 나중에 고치자 하고 넘어가는 임시 방편인 것입니다.
또는 스스로 직렬 큐 등을 사용해 접근을 이미 직렬화 해놨을 때 컴파일러는 이 사실을 알지 못합니다.
이 경우 내가 보장하니 검사를 꺼달라는 식으로 사용도 가능합니다.
오늘은 이렇게 nonisolated에 대해 알아보았습니다.
감사합니다~
'Swift' 카테고리의 다른 글
| Swift - WebRTC란? (0) | 2026.07.19 |
|---|---|
| Swift - API 호출 실패 시 재시도 (0) | 2026.07.07 |
| Swift - 캐시 지역성(Cache Locality)이란 (0) | 2026.06.29 |
| Swift - Structured Concurrency(구조적 동시성) (0) | 2026.06.25 |
| Swift - Swift Concurrency (0) | 2026.06.23 |
- Total
- Today
- Yesterday
- 알고리즘
- BoxedType
- Tuist
- Algorithm
- iphone
- opaque
- tuist v4
- SwiftUI
- coredata
- Xcode
- Actor
- AI
- 코어데이터
- SocialLogin
- opaquetype
- 경사하강법
- GradientDescent
- CacheLocality
- ios
- 동시성
- 인공신경망
- Concurrency
- 스위프트
- swift
- kakaomapssdk
- nonisolated
- webrtc
- CD/CI
- 역전파
- ViewBuilder
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 | 31 |
