ProgrammingiOS開発者

Swiftで変更可能なデータに対してスレッドセーフな相互作用を実現するにはどうすればよいですか?不適切な実装によって発生する可能性のあるアプローチと問題について説明してください。

Hintsage AIアシスタントで面接を突破

回答

Swiftでは、スレッドセーフを確保するために、主にGCD(Grand Central Dispatch)やディスパッチキュー(DispatchQueue)、NSLock、およびSwift 5.5以降のactorなどの最新のメカニズムが使用されます。

基本的な考え方 – 変更可能なデータへのアクセスは、厳密に直列キューを介して行うか、または同期化装置(ロック)を使用します。

GCDの例:

class ThreadSafeArray<Element> { private var array: [Element] = [] private let queue = DispatchQueue(label: "com.example.arrayQueue", attributes: .concurrent) func append(_ item: Element) { queue.async(flags: .barrier) { self.array.append(item) } } func get(index: Int) -> Element? { var result: Element? queue.sync { result = self.array.indices.contains(index) ? self.array[index] : nil } return result } }

現代的なアプローチ: Actors (Swift 5.5+):

actor SafeCounter { private var value = 0 func increment() { value += 1 } func get() -> Int { return value } }

どちらのアプローチもデータ競合を避け、状態の一貫性を維持することを可能にします。

トリックの質問

質問:

メインキュー内でDispatchQueue.syncを使用した場合、何が起こるか、なぜですか?

回答: デッドロックが発生します(Deadlock)、なぜならメインキューはすでにタスクを実行中であり、同じキュー上の同期操作の完了を待っているからです。その結果、キューは現在のタスクが完了するまで次のタスクを処理できなくなりますが、それは決して起こりません。

例:

DispatchQueue.main.sync { // デッドロック: この行は絶対に実行されません }

テーマの詳細を知らなかったための実際のエラーの例


物語

プロジェクトでは、複数のスレッドから同期なしで同時にアクセスされる可変集合が使用されていました。これにより、アプリケーションがクラッシュし、データ競合による捕らえどころのないバグが発生しました: 時折、要素が失われ、時には配列の範囲を超えることがありました。


物語

ある開発者が、ランタイムでメインキューに対してDispatchQueue.syncを不正に使用したため、デッドロックが発生し、ユーザーのUIが完全に停止しました。緊急の修正とリリースのロールバックが必要でした。


物語

NSLockを使用してクラスをスレッドセーフにしようとした際、メソッドのすべての出口パス(エラーの場合やguard内のreturnなど)に対してロック/アンロックを実装するのを忘れたため、デッドロックの可能性が生じ、アプリケーションのパフォーマンスに重大な問題が発生しました。