<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์‹œ