<h1><center> iOS interview 6 </center></h1>
###### tags: `๐ป ๋ฉด์ ์ง๋ฌธ`
###### date: `2025-10-0717:21:33.284Z`
> [iOS interview](https://github.com/JeaSungLEE/iOSInterviewquestions)
# Concurrency
### ๊ฒฉ๋ฆฌ/๋น๊ฒฉ๋ฆฌ
- ๊ฐ ์์
์ด Task ๋ง๋ค ๊ณ ์ ์ฑ์ ์งํจ๋ค.
- Actor isolation vs Task isolation
- Task isolation - Task ๋ฉํ๋ฐ์ดํฐ ๊ด๋ จ ์ด์ผ๊ธฐ, **Task.detached**
- ์์
๊ฐ Share Mutable State๋ฅผ ํ์ฉํ์ง ์๊ธฐ ์ํจ
- Actor isolation - ์ด๋ฒ์ ์์๋ณด์...
## Actor
> ์กํฐ๊ฐ ๋ญ์ง? ์ ๋์์ง?
#### ์ ๋์์ง?
Data race ํด๊ฒฐ..? => Shared mutable state (๊ณต์ ๊ฐ๋ณ ์ํ -> ์ฌ๋ฌ ๊ณณ์์ ๋์์ ์์ ๊ฐ๋ฅํ ๋ฐ์ดํฐ) ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์
=> ๋ด๋ถ ์ ์ฅ ์์ฑ์ ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์
```swift
class A { var c: Int = 0 }
let a = A()
Task { a.c = 1 }
Task { a.b = 2 }
```
> Shared mutable state
#### ๋งค์ปค๋์ฆ
1. ํ๋์ ์ฐ๋ ๋์์๋ง ์ ๊ทผ์ ๋ณด์ฅํ๋ค -> ์๋ ์ง๋ ฌํ
2. ๊ณต์ ๊ฐ๋ณ ์ํ์ ์ ๊ทผ์ ์ง๋ ฌ ์คํ์(Serial Executor)๋ฅผ ์ด์ฉํด ์ง๋ ฌํ์ํจ๋ค. // ์๋ก ๋์จ ๊ฐ๋
{%preview https://developer.apple.com/documentation/swift/serialexecutor %}
#### ๋ฌธ๋ฒ์ ์ถ๊ฐ์ ์ธ ๋ด์ฉ
1. ์์์ด ์๋จ - ๊ฐ๊ฐ์ ํ์
๋ง๋ค ๊ณ ์ ํ ์ฐ๋ ๋๋ฅผ ์ง์ผ์ผํ๋๊น... Why??
- (์ ํํ๊ฑด ์๋์ง๋ง)
- ๋ชจ๋ actor๋ ์์ ๋ง์ executor๋ฅผ ๊ฐ์ ธ์ผ ํ๋ค..
- ํ์ง๋ง ์์์ด ๋๋ฉด ์์ ๊ฐ์ฒด์์ ๋ถ๋ชจ์ property ๋ฅผ ์ ๊ทผํ๊ฑฐ๋ ํ ๋ ์ด๋ค executor์์ ์คํ๋์ด์ผํ ์ง ๋ชจ๋ฆ..!
- ๋ถ๋ชจ์ ์์์ด ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ๊ณต์ ํ๋๋ฐ executor๊ฐ ๋ค๋ฅด๊ฒ ๋ ์ ์์
2. ํ์ ์ ์ฅ๋๋ ์ฐธ์กฐ ํ์
3. ํ๋กํ ์ฝ ์ฑํ์ ๊ฐ๋ฅํ๋ค. + **Actor ํ๋กํ ์ฝ์ ์์์ ์ผ๋ก ์ฑํํ๋ค.**
```swift
class Parent {
let a: Int = 1
init() { }
}
class Child: Parent {
let b: Int = 2
override init() {
super.init()
}
func plusAB() -> Int {
return a + b;
}
}
// Child -> Parent(a)
// Child(a)
actor A { } // ??? A : Actor { }
```
(ํํ)
์๋ ์ง๋ ฌํ? ๋๊ธฐํ? NSLock ๊ฐ์ ๊ฑธ๋ก ๋ฎคํ
์ค ์ฒ๋ฆฌ, ์ธ๋งํฌ์ด๋ก ๋๊ธฐํ๋ฅผ ์ฒ๋ฆฌํ์ง ์๊ณ actor๋ฅผ ์ฌ์ฉํ๋ฉด ์๋์ผ๋ก ์ง๋ ฌํ๋ฅผ ๋ณด์ฅํ๊ฒ ๋ง๋ค ์ ์๋ค?
### actor์ isolation๊ณผ nonisolation
> ์กํฐ๊ฐ ์ธ๋ถ์ ์ด๋ป๊ฒ ํต์ ํ ์ง์ ๋ํ ๋ด์ฉ
๊ธฐ๋ณธ์ ์ผ๋ก ์กํฐ๋ isolation์ด ์ง์ผ์ง๋ค. -> ์กํฐ ์ธ๋ถ์์ ์กํฐ๋ฅผ ์ ๊ทผํ๋ ค๊ณ ํ๋ฉด, ๋น๋๊ธฐ๋ก ์ ๊ทผํด์ผํจ
```swift
actor A {
nonisolated let idNumber: Int = 0 // (var๋ ๋ ์ ์๋ค... ๊ณต์ ๊ฐ๋ณ ์ํ๊ฐ ๋๋๊น...)
nonisolated var computedNumber: Int {
get { 3 }
} // ์ด๊ฑด ๋๋ ค๋..? ๋๋ค..!
// (์์ง)
nonisolated var displayName: String {
// idNumber๊ฐ nonisolated๊ฐ ์๋๋ผ๋ฉด ์ ๊ทผ ๋ถ๊ฐ
// property a๋ ์ ๊ทผ ๋ถ๊ฐ (์๋ํ๋ฉด ์๊ธฐ ์์ ์ ์ฐธ์กฐํ ์ ์๋ค -> ๊ฒฉ๋ฆฌ๋ ๊ฐ์๋ ์ ๊ทผ ๋ถ๊ฐ)
// nonisolated ํ๋ฉด ์ ๊ทผ ๊ฐ๋ฅ
// ๋ณ์ ์ ๊ทผ ์, Actor-isolated property 'a' can not be referenced from a nonisolated context
return "Actor a \(a)"
}
var a: Int = 3
var displayName: String { // isolated ํ๊ฑฐ...
get async { "๋ด๊ฐ ๋ณด์ด๋..." }
}
nonisolated func run() async { }
}
// ์งค๋งํ ์์ฉ
extension A: Hashable {
nonisolated func hash(into hasher: inout Hasher) { }
}
class Runner {
let a = A()
func run() async {
await a.run()
}
func parellalRun() async {
await withTaskGroup(of: Void.self) { group in
for i in 0..<10 {
group.addTask {
await a.run() // nonisolated์์ผ๋ฉด ์ด์ฐจํผ A์กํฐ ํ ๊ณณ์์ run์ ์ฒ๋ฆฌํจ
}
}
}
}
}
```
=> ํ๋์ ์ฐ๋ ๋(actor A์ ์ฐ๋ ๋...)๋ง run์ ์คํ์ํจ๋ค.
- ๋ฐ์ ๊ฐ๋ฅํ ๋ฌธ์
- ์ฑ๋ฅ์์ ๋ฌธ์ ๋ฐ์..! - ๋ณ๋ ฌ ์ฒ๋ฆฌ๊ฐ ๋์ง ์์
#### ์ผ๋ฐ์ ์ธ ๋ณ์ ์ ์ฅ ํ๋กํผํฐ๋ ์๋๋ค... ์์๋ ๋จ..!
### ์์์ฑ
DB์์ ์ฃผ๋ก ๋ค๋ฃจ๋ ๋ด์ฉ - ํ๋์ ์์
์ ์ชผ๊ฐค ์ ์๋ ๋จ์๋ก ์ํค๋ ๊ฒ - Transaction
> In computer science, "atomic" describes operations that are indivisible, meaning they must complete entirely or not at all, preventing any observable intermediate states.
> ์ฆ, ์์ ํ ์๋ฃ๋๊ฑฐ๋ ์ ํ ์ํ๋์ง ์์์ ์๋ฏธ
๋ด๋ถ ์ ์ฅ ํ๋กํผํฐ์ ํ ์ฐ๋ ๋ ์ ๊ทผ๋ง ๋ณด์ฅํ๋๊ฒ
Actor๋ฅผ ์ฐ๋ฉด ์์์ฑ์ ๋ณด์ฅํ๋ค..? ๊ผญ ๊ทธ๋ ์ง๋ง์ ์๋ค. ๊ทธ๋์ ์ฃผ์ํด์ผํ ์ ์ด ์๋ค.
```swift
actor A {
var a: [Int] = []
/// ์์์ฑ์ด ๊นจ์ง๋ ๊ฒฝ์ฐ
func run(num: Int) async {
var copyA = await a; // ์ฌ๊ธฐ์ actor A ์์
์ด ๋ฉ์ถค
//(์์ง) ํน์ ๋ฉ์ถ๋ ๊ฒ ์์ฒด๊ฐ ์ค๊ฐ์ ๋ค๋ฅธ ๋์์ด ๊ฐ๋ฅํด์ ธ์ ์์์ฑ์ด ๊นจ์ง๋๊ฑด๊ฐ? ์ํ ์ดํด ์.
copyA.append(num); // copyA์ ๊ฐ์ด a์ ๊ฐ๊ณผ ๋ฌ๋ผ์ง ์ ์๋ค.
a = await copyA;
}
// [1, 2]
// -> 1: copyA: [] - [1] | 2: copyA: [] - [2]
// result: a -> [1] || [2]
// ๋ด๋ถ๋ ๋๊ธฐ์ ์ผ๋ก ์์
ํ๋๊ฒ ์ข๋ค..! ํน์ ๊ทธ์ ์์ํ๋(๋ณด์ฅ) ์ฒ๋ฆฌ๋ฅผ ํ๊ธฐ
func atomicRun(num: Int) {
a.append(num);
}
}
let a = A()
// ์ธ๋ถ์์ Atomic ๋ณด์ฅ
func addOne(num: Int, a: isolated A) { // ์ด ํจ์ ์ค์ฝํ๊ฐ A actor๋ฅผ ๋ฐ๋ฆ
a.a.append(num) // asnyc-await ์์ด ๋๊ธฐ์ ์ผ๋ก ์๋ ํด์ผํจ...
}
// Task ๋ด๋ถ๋ isolated๊ฐ ์ ์ฉ๋์ด์์
Task(isolated...) {
}
```
### ์ฌ์ง์
-> ์กํฐ ๋ฉ์๋ ๋ด๋ถ์์ ์ผ์์ค๋จ ์ดํ ๋ฐ์ดํฐ ๋ณ๊ฒฝ ๊ฐ๋ฅ์ฑ
```swift
// MARK: - Task๋ฅผ ์ด์ฉํ ์ฌ์ง์
actor TaskImageDownloader {
enum DownloadState {
case downloading(task: Task<UIImage, Error>)
case completed(image: UIImage)
case failed(error: Error)
}
private(set) var cache = [String: DownloadState]()
func downloadImage(url: String) async -> UIImage {
if let state = cache[url] {
return switch state {
case .downloading(let task):
return await task.value
case .completed(let image):
return image
case .failed(let error):
throw error
}
}
let task = Task.detached<UIImage, Error> {
return await downloadImageFromNetwork(url: url)
}
// ๋ค์ด๋ก๋ ์ค์ธ ์ํ๋ฅผ ์ ์ฅ
cache[url] = .downloading(task: task)
do {
let image = try await task.value
cache[url] = .completed(image: image)
return image
} catch {
cache[url] = .failed(error: error)
throw error
}
return await task.value
}
private func downloadImageFromNetwork(url: String) async throws -> UIImage {
let (data, response) = try await URLSession.shared.data(from: URL(string: url)!)
return UIImage(data: data)!
}
}
```
### ์กํฐ ํํ
-> ๊ฐ๊ฐ ๋ค๋ฅธ ์กํฐ๊ฐ์ ์ ๊ทผ์์ ๋ฐ์ํ๋ ์์
์ ํ ์ค๋ฒํค๋ -> context switching
> ์กํฐ๋ฅผ ๋ง์ด ์ฌ์ฉํ๋ค๊ณ ๋ฅ์ฌ๊ฐ ์๋๋ค... ๋น๊ฒฉ๋ฆฌ์ฌ๋ ๊ด์ฐฎ์ ๊ฒ์ด๋ฉด class, nonisolated๋ฅผ ์ฐ๋ context switch๋ฅผ ๋ฎ์ถ๋ค. (์กํฐ ํํ)
ํน์ ๊ฐ์ฒด๋ฅผ ๋ค๋ฅธ **actor์** ์ข
์์ํจ๋ค.
``` swift
@globalActor
actor Aactor {
static let shared = Aactor()
}
actor Run {
var aActor: Aactor
}
@Aactor // ๊ธ๋ก๋ฒ ์กํฐ
class Run {
var aActor: Aactor!
}
```
### ๊ธ๋ก๋ฒ ์กํฐ
@globalActor | actor๋ฅผ ํ๋์ ๊ฐ์ฒด ๋ฟ๋ง ์๋๋ผ ์ ์ญ์ ์ผ๋ก ์ฑํํ ์ ์๊ฒ ํด์ฃผ๋ ํ์
+ ํน์ ๊ฐ์ฒด, ๋ฉ์๋, ํ๋กํผํฐ๋ฅผ ํน์ Serial Executor์์๋ง ์คํ์ํฌ ์ ์๊ฒ ๋ง๋ค์ด์ค๋ค.
ex) @MainActor
Serial Executor - **ํ๋์ ์ฐ๋ ๋๋ก์ ์์
๋ณด์ฅ๋๋๊ฒ** ์ข์ ๊ฒฝ์ฐ
1. View ๋ ๋๋ง
2. DB Transaction
3. Audio - Node + Node + Node => AudioEngine -> Speaker
#### globalActor ๋ง๋ค๊ธฐ...
@globalActor ํ๋กํผํฐ ๋ํผ ์ฑํ์, GlobalActor ํ๋กํผํฐ๋ฅผ ์ฑํํจ
- ์ฑ๊ธํค ํจํด์ ์ ์ฉํ๋ค.
``` swift
@globalActor
actor Aactor {
static let shared = Aactor()
}
@MainActor // ๊ฐ๊ฟ...
```
1. Shared mutate state
2. ์ด๋ป๊ฒ? ๋จ์ผ ์ฐ๋ ๋ ์ ๊ทผ์ ๋ณด์ฅํด์
3. ๊ทธ๋์ ์ด๋ค ๋ฌธ์ ๋ค์ด..?
- isolate/nonisolated, hoping, reenterency, atomicity, globalActor...
> ํ๋ฌ๋ค์ด๊ฐ๋ค ๋ฐ๊ฒฌ
> https://github.com/swiftlang/swift-evolution/blob/main/proposals/0306-actors.md
# Combine
(์ฌ๊ธ ๋ด)
- ๊ฐ์: https://www.inflearn.com/course/combine-reactive-programming
### ๋ค์์ฃผ
?
Concurrency ๋ง๋ฌด๋ฆฌ -> iOS ์ง๋ฌธ ๋ต๋ณ ์ฌ๊ฐ
์ผ์์ผ ์ค์ 9์