[G1-Sync] Manual knowledge update

This commit is contained in:
Antigravity Agent
2026-05-09 21:08:02 +09:00
parent f0befc887a
commit 93ec7e9056
363 changed files with 68333 additions and 64 deletions
@@ -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]]