[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
---
|
||||
id: ios-swift-concurrency-async-await
|
||||
title: Swift Concurrency — async/await + actors
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [ios, swift, concurrency, async-await, actor, vibe-coding]
|
||||
tech_stack: { language: "Swift 5.5+", applicable_to: ["iOS", "macOS", "watchOS"] }
|
||||
applied_in: []
|
||||
aliases: [structured concurrency, Task, MainActor, actor isolation]
|
||||
---
|
||||
|
||||
# Swift Concurrency
|
||||
|
||||
> Swift 의 async/await + Task + Actor 는 **structured concurrency**. callback hell 끝. 핵심: (1) Task 트리는 부모 cancel = 자식 cancel, (2) UI 업데이트는 `@MainActor`, (3) 공유 mutable 상태는 actor.
|
||||
|
||||
## 📖 핵심 개념
|
||||
- `async` 함수: suspension 가능 함수.
|
||||
- `await`: suspend 지점.
|
||||
- `Task`: 동시성 단위. 부모-자식 구조.
|
||||
- `actor`: 격리된 가변 상태. 한 시점 한 메서드만 실행.
|
||||
- `@MainActor`: UI 스레드에서 실행 보장.
|
||||
|
||||
## 💻 코드 패턴
|
||||
|
||||
### 기본 async fetch
|
||||
```swift
|
||||
func loadUser(id: String) async throws -> User {
|
||||
let (data, _) = try await URLSession.shared.data(from: URL(string: "https://api.example.com/users/\(id)")!)
|
||||
return try JSONDecoder().decode(User.self, from: data)
|
||||
}
|
||||
|
||||
Task {
|
||||
do {
|
||||
let user = try await loadUser(id: "1")
|
||||
await MainActor.run { self.user = user }
|
||||
} catch {
|
||||
print("failed: \(error)")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### MainActor + ViewModel
|
||||
```swift
|
||||
@MainActor
|
||||
final class UserViewModel: ObservableObject {
|
||||
@Published var user: User?
|
||||
@Published var isLoading = false
|
||||
|
||||
func load(id: String) async {
|
||||
isLoading = true
|
||||
defer { isLoading = false }
|
||||
do { user = try await loadUser(id: id) }
|
||||
catch { /* surface error */ }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 동시 실행 — async let
|
||||
```swift
|
||||
func loadProfile(id: String) async throws -> Profile {
|
||||
async let user = loadUser(id: id)
|
||||
async let posts = loadPosts(id: id)
|
||||
return Profile(user: try await user, posts: try await posts)
|
||||
}
|
||||
```
|
||||
|
||||
### Task cancellation
|
||||
```swift
|
||||
let task = Task {
|
||||
for i in 0..<1000 {
|
||||
try Task.checkCancellation() // cancel 시 throw
|
||||
await process(i)
|
||||
}
|
||||
}
|
||||
// 나중에
|
||||
task.cancel()
|
||||
```
|
||||
|
||||
### Actor — 격리된 상태
|
||||
```swift
|
||||
actor Counter {
|
||||
private var value = 0
|
||||
func increment() { value += 1 }
|
||||
func current() -> Int { value }
|
||||
}
|
||||
|
||||
let c = Counter()
|
||||
await c.increment()
|
||||
let v = await c.current()
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준
|
||||
| 작업 | 도구 |
|
||||
|---|---|
|
||||
| 단순 비동기 호출 | async/await |
|
||||
| 두 비동기 동시 + 모두 기다림 | async let |
|
||||
| 여러 동시 + 결과 collect | TaskGroup |
|
||||
| UI 업데이트 | @MainActor |
|
||||
| 공유 가변 상태 | actor |
|
||||
| Timer / Combine 호환 | AsyncStream |
|
||||
| Cancel 가능 작업 | Task + checkCancellation |
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Task { } 안에서 self 강한 참조**: 메모리 leak. `[weak self]` 사용.
|
||||
- **DispatchQueue 와 async/await 혼용**: 콜백 → continuation → 옛 패턴. withCheckedContinuation 으로 통합.
|
||||
- **MainActor 메서드를 background 에서 직접 호출**: 컴파일 에러 또는 런타임 violation. await + MainActor 명시.
|
||||
- **actor 메서드 안에서 long-running 동기 작업**: 다른 호출 차단. async 로 분할.
|
||||
- **Task 안에서 try? 로 에러 silently 삼킴**: 디버깅 지옥. 최소 logging.
|
||||
- **structured concurrency 깨기 (`Task.detached`)**: 부모-자식 구조 끊김. 정말 독립 작업일 때만.
|
||||
- **for await loop 가 너무 길어서 cancel 무시**: checkCancellation 정기 호출.
|
||||
|
||||
## 🤖 LLM 활용 힌트
|
||||
- "@MainActor 로 ViewModel 마크. UI 업데이트는 자동 main thread" 명시.
|
||||
- 공유 상태는 actor, UI 는 MainActor, 백그라운드 작업은 일반 async.
|
||||
- Task.detached 는 거의 안 씀.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[iOS_Swift_Result_Type]]
|
||||
- [[iOS_Swift_Memory_ARC_Cycles]]
|
||||
Reference in New Issue
Block a user