คำถามสัมภาษณ์ Swift Structured Concurrency: async/await, TaskGroup, Actors

คำถามสัมภาษณ์เชิงเทคนิค Swift Structured Concurrency: async/await, TaskGroup, actors และรูปแบบ concurrency สำหรับ iOS 2026

คำถามสัมภาษณ์ Swift Structured Concurrency พร้อม async/await, TaskGroup และ actors

Structured Concurrency ที่เปิดตัวใน Swift 5.5 ได้ปฏิวัติการเขียนโปรแกรมแบบอะซิงโครนัสบน iOS ผู้สรรหาบุคลากรในปัจจุบันทดสอบความเชี่ยวชาญใน async/await, TaskGroup และ actors ในระหว่างการสัมภาษณ์เชิงเทคนิค ต่อไปนี้คือคำถามสำคัญและคำตอบที่คาดหวังเพื่อสร้างความโดดเด่นในการสัมภาษณ์

ทักษะหลักที่ทดสอบในการสัมภาษณ์

ผู้สรรหาประเมินสามทักษะ: ความเข้าใจในแนวคิดพื้นฐาน (async/await, Task), ความเชี่ยวชาญในรูปแบบ concurrency (TaskGroup, actor isolation) และความสามารถในการวินิจฉัยข้อผิดพลาดที่พบบ่อย (data races, deadlocks)

ความแตกต่างระหว่าง async/await และ DispatchQueue คืออะไร?

คำตอบที่คาดหวัง: async/await ให้ structured concurrency ด้วยโค้ดเชิงลำดับที่อ่านง่าย ในขณะที่ DispatchQueue ใช้ callback และอาจนำไปสู่สิ่งที่เรียกว่า "callback hell" Swift จัดการเธรดโดยอัตโนมัติด้วย async/await

NetworkService.swiftswift
// Comparison: async/await vs DispatchQueue

// ❌ Old style: DispatchQueue with callbacks
func fetchUserOld(id: Int, completion: @escaping (Result<User, Error>) -> Void) {
    DispatchQueue.global().async {
        // Simulated network call
        let result = self.performNetworkRequest(id: id)
        DispatchQueue.main.async {
            completion(result)
        }
    }
}

// ✅ New style: async/await more readable
func fetchUser(id: Int) async throws -> User {
    // Swift runtime handles threads automatically
    // No need to manually switch between queues
    return try await performNetworkRequest(id: id)
}

ประเด็นสำคัญ: async/await ขจัด callback pyramid ลดข้อผิดพลาดของเธรด (ไม่ต้อง DispatchQueue.main.async) และอนุญาตให้ Swift runtime เพิ่มประสิทธิภาพการดำเนินการในคอร์ CPU ที่มี

ข้อได้เปรียบด้านประสิทธิภาพ

Swift runtime ใช้ thread pool ที่ปรับให้เหมาะสมซึ่งหลีกเลี่ยงการสร้างเธรดมากเกินไป ต่างจาก DispatchQueue ที่แต่ละ .async อาจสร้างเธรดใหม่ async/await นำเธรดที่มีอยู่กลับมาใช้ใหม่อย่างชาญฉลาด

จะจัดการการดำเนินการอะซิงโครนัสหลายรายการแบบขนานได้อย่างไร?

คำตอบที่คาดหวัง: ใช้ async let สำหรับงาน 2-3 งานที่เรียบง่าย หรือ TaskGroup สำหรับจำนวนงานขนานแบบไดนามิกพร้อมการรวบรวมผลลัพธ์

DataFetcher.swiftswift
// Parallel async operations strategies

struct DataFetcher {
    // Strategy 1: async let for fixed tasks (2-4 operations)
    func loadDashboard() async throws -> Dashboard {
        // Launch 3 requests in parallel
        async let user = fetchUser()
        async let posts = fetchPosts()
        async let notifications = fetchNotifications()

        // Wait for results (parallel, not sequential)
        let (userData, postsData, notificationsData) = try await (user, posts, notifications)

        return Dashboard(user: userData, posts: postsData, notifications: notificationsData)
    }

    // Strategy 2: TaskGroup for dynamic number of tasks
    func downloadImages(urls: [URL]) async throws -> [UIImage] {
        // TaskGroup allows managing N tasks with result collection
        try await withThrowingTaskGroup(of: (Int, UIImage).self) { group in
            // Launch one task per URL
            for (index, url) in urls.enumerated() {
                group.addTask {
                    let (data, _) = try await URLSession.shared.data(from: url)
                    guard let image = UIImage(data: data) else {
                        throw ImageError.invalidData
                    }
                    return (index, image) // Return index to preserve order
                }
            }

            // Collect results in order
            var images = [UIImage?](repeating: nil, count: urls.count)
            for try await (index, image) in group {
                images[index] = image
            }

            return images.compactMap { $0 }
        }
    }
}

ข้อผิดพลาดทั่วไป: การใช้ await ตามลำดับแทนที่จะใช้ async let ซึ่งทำให้การเรียกขนานกัน let user = await fetchUser(); let posts = await fetchPosts() ดำเนินการตามลำดับ (ช้า) ในขณะที่ async let เปิดทั้งสองพร้อมกัน

Actor คืออะไรและทำไมต้องใช้?

คำตอบที่คาดหวัง: Actor เป็นประเภทที่ปกป้องสถานะที่เปลี่ยนแปลงได้จาก data races โดยรับประกันการเข้าถึงตามลำดับ ใช้แทน lock แบบแมนนวล (NSLock, DispatchQueue) เพื่อรักษาความปลอดภัยของการเข้าถึงพร้อมกัน

CacheManager.swiftswift
// Actor for thread-safe state management

// ❌ Classic class: data race risk
class UnsafeCache {
    private var cache: [String: Data] = [:] // Not thread-safe!

    func store(_ data: Data, for key: String) {
        cache[key] = data // ⚠️ Race condition with concurrent access
    }
}

// ✅ Actor: automatic protection against races
actor SafeCache {
    private var cache: [String: Data] = [:]

    // Sequential access guaranteed by actor isolation
    func store(_ data: Data, for key: String) {
        cache[key] = data // ✅ Thread-safe automatically
    }

    func retrieve(for key: String) -> Data? {
        return cache[key] // ✅ Protected read
    }

    // Internal synchronous method (no await needed)
    nonisolated func clearAll() async {
        // nonisolated allows calling from any context
        await self.clear()
    }

    private func clear() {
        cache.removeAll()
    }
}

// Usage: await required to access actor
let cache = SafeCache()
await cache.store(data, for: "user_123") // Await mandatory
let cachedData = await cache.retrieve(for: "user_123")

ประเด็นสำคัญ: Actor รับประกันว่ามีเธรดเดียวเข้าถึงสถานะของมันในเวลาเดียวกัน คอมไพเลอร์บังคับใช้ await สำหรับการเรียกภายนอก ทำให้จุดพักที่อาจเกิดขึ้นชัดเจน

กับดัก MainActor

@MainActor เป็น actor ระดับโลกสำหรับการดำเนินการ UI การทำเครื่องหมายคลาสด้วย @MainActor บังคับให้เมธอดทั้งหมดของมันทำงานบน main thread ระวังการเรียกที่บล็อกซึ่งอาจทำให้ส่วนติดต่อค้าง

พร้อมที่จะพิชิตการสัมภาษณ์ iOS แล้วหรือยังครับ?

ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ

จะจัดการข้อผิดพลาดใน TaskGroup ได้อย่างไร?

คำตอบที่คาดหวัง: withThrowingTaskGroup ส่งต่อข้อผิดพลาดแรกที่พบและยกเลิกงานที่เหลืออัตโนมัติ ในการรวบรวมข้อผิดพลาดทั้งหมด ใช้ Result ใน TaskGroup

BatchProcessor.swiftswift
// Error handling strategies in TaskGroup

struct BatchProcessor {
    // Strategy 1: First error propagation (fail-fast)
    func processItemsFastFail(items: [Item]) async throws -> [ProcessedItem] {
        try await withThrowingTaskGroup(of: ProcessedItem.self) { group in
            for item in items {
                group.addTask {
                    // If one task throws, group cancels others
                    try await self.process(item)
                }
            }

            // Collect results until first error
            var results: [ProcessedItem] = []
            for try await result in group {
                results.append(result)
            }
            return results
        }
        // ⚠️ If one task fails, others are cancelled
    }

    // Strategy 2: Collect all errors (resilience)
    func processItemsResilient(items: [Item]) async -> ([ProcessedItem], [Error]) {
        await withTaskGroup(of: Result<ProcessedItem, Error>.self) { group in
            for item in items {
                group.addTask {
                    // Wrap in Result to capture errors
                    do {
                        let result = try await self.process(item)
                        return .success(result)
                    } catch {
                        return .failure(error)
                    }
                }
            }

            // Separate successes/failures
            var successes: [ProcessedItem] = []
            var errors: [Error] = []

            for await result in group {
                switch result {
                case .success(let item):
                    successes.append(item)
                case .failure(let error):
                    errors.append(error)
                }
            }

            return (successes, errors)
        }
    }

    private func process(_ item: Item) async throws -> ProcessedItem {
        // Processing with possible error
        try await Task.sleep(nanoseconds: 100_000_000)
        return ProcessedItem(from: item)
    }
}

ประเด็นสำคัญ: withThrowingTaskGroup หยุดที่ข้อผิดพลาดแรก (มีประโยชน์สำหรับการดำเนินการแบบอะตอมิก) ในขณะที่ withTaskGroup + Result อนุญาตให้ดำเนินการต่อแม้จะมีข้อผิดพลาด (มีประโยชน์สำหรับการประมวลผลแบบ batch)

ความแตกต่างระหว่าง Task, Task.detached และ async let คืออะไร?

คำตอบที่คาดหวัง: Task สืบทอดบริบทของ parent (ลำดับความสำคัญ, actor isolation), Task.detached สร้างงานอิสระโดยไม่สืบทอด และ async let สร้าง child task ที่รออัตโนมัติเมื่อจบ scope

TaskLifecycle.swiftswift
// Understanding Task creation patterns

@MainActor
class ViewModel {
    var isLoading = false

    // Scenario 1: Task inherits context (@MainActor here)
    func loadDataWithTask() {
        Task {
            // ✅ Inherits @MainActor from parent
            // No need for await MainActor.run
            self.isLoading = true
            let data = try await fetchData()
            self.isLoading = false // ✅ Always on MainActor
        }
    }

    // Scenario 2: Task.detached creates independent task
    func loadDataDetached() {
        Task.detached {
            // ⚠️ Does NOT inherit @MainActor
            let data = try await self.fetchData()

            // ❌ Error: isLoading not directly accessible
            // await MainActor.run {
            //     self.isLoading = false
            // }
        }
    }

    // Scenario 3: async let creates structured child task
    func loadMultipleData() async throws {
        // async let tasks are bound to current scope
        async let users = fetchUsers()
        async let posts = fetchPosts()

        // ⚠️ If leaving function before await, compilation error
        let (usersData, postsData) = try await (users, posts)

        // async let tasks automatically cancelled
        // if exiting scope (e.g., throw before await)
    }

    private func fetchData() async throws -> Data {
        try await URLSession.shared.data(from: URL(string: "https://api.example.com")!).0
    }

    private func fetchUsers() async throws -> [User] { [] }
    private func fetchPosts() async throws -> [Post] { [] }
}

กรณีการใช้งาน:

  • Task: การดำเนินการที่ผูกกับบริบทปัจจุบัน (เช่น การอัปเดต UI จาก ViewModel)
  • Task.detached: งานเบื้องหลังที่อิสระ (เช่น log, analytics)
  • async let: การดำเนินการแบบขนานที่ต้องการผลลัพธ์ใน scope ปัจจุบัน

จะใช้งาน timeout บนการดำเนินการอะซิงโครนัสได้อย่างไร?

คำตอบที่คาดหวัง: ใช้ Task.sleep ในการแข่งขันระหว่างงานหลักและงาน timeout ด้วย withThrowingTaskGroup หรือสร้างยูทิลิตี้ withTimeout

AsyncTimeout.swiftswift
// Timeout implementation for async operations

enum TimeoutError: Error {
    case timedOut
}

// Generic utility to add timeout
func withTimeout<T>(
    seconds: TimeInterval,
    operation: @escaping () async throws -> T
) async throws -> T {
    try await withThrowingTaskGroup(of: T.self) { group in
        // Task 1: main operation
        group.addTask {
            try await operation()
        }

        // Task 2: timeout
        group.addTask {
            try await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))
            throw TimeoutError.timedOut
        }

        // First task to finish wins
        guard let result = try await group.next() else {
            throw TimeoutError.timedOut
        }

        // Cancel losing task (important for cleanup)
        group.cancelAll()

        return result
    }
}

// Usage example
struct NetworkService {
    func fetchUserWithTimeout(id: Int) async throws -> User {
        // 5-second timeout on network call
        try await withTimeout(seconds: 5) {
            try await self.fetchUser(id: id)
        }
    }

    private func fetchUser(id: Int) async throws -> User {
        let url = URL(string: "https://api.example.com/users/\(id)")!
        let (data, _) = try await URLSession.shared.data(from: url)
        return try JSONDecoder().decode(User.self, from: data)
    }
}

ทางเลือกสมัยใหม่: ตั้งแต่ iOS 16 ใช้ URLSession ด้วย timeoutInterval ที่กำหนดค่าผ่าน URLSessionConfiguration โดยเฉพาะสำหรับการเรียก HTTP

การยกเลิกที่ชัดเจน

group.cancelAll() มีความสำคัญในการปลดปล่อยทรัพยากร หากไม่มีสิ่งนี้ งานที่แพ้จะดำเนินการต่อในเบื้องหลังจนกว่าจะเสร็จสิ้นโดยธรรมชาติ ทำให้สิ้นเปลือง CPU และหน่วยความจำ

จะแบ่งปันสถานะที่เปลี่ยนแปลงได้อย่างปลอดภัยระหว่างงานหลายอย่างได้อย่างไร?

คำตอบที่คาดหวัง: ใช้ actor สำหรับสถานะที่แบ่งปัน หรือ AsyncStream เพื่อสื่อสารระหว่างงานผ่านสตรีมของค่า

SharedStateManager.swiftswift
// Safe state sharing between concurrent tasks

// Approach 1: Actor for shared state with sequential access
actor DownloadManager {
    private var activeDownloads: [String: Task<Data, Error>] = [:]
    private var cache: [String: Data] = [:]

    // Start download or return existing task
    func download(url: String) async throws -> Data {
        // Check cache first
        if let cachedData = cache[url] {
            return cachedData
        }

        // Check if download already in progress
        if let existingTask = activeDownloads[url] {
            return try await existingTask.value
        }

        // Create new download task
        let task = Task<Data, Error> {
            let data = try await self.performDownload(url: url)

            // Update cache (thread-safe via actor)
            await self.completeDownload(url: url, data: data)

            return data
        }

        activeDownloads[url] = task
        return try await task.value
    }

    private func performDownload(url: String) async throws -> Data {
        let urlObject = URL(string: url)!
        let (data, _) = try await URLSession.shared.data(from: urlObject)
        return data
    }

    private func completeDownload(url: String, data: Data) {
        cache[url] = data
        activeDownloads.removeValue(forKey: url)
    }
}

// Approach 2: AsyncStream for inter-task communication
struct EventStream {
    private let continuation: AsyncStream<Event>.Continuation
    let stream: AsyncStream<Event>

    init() {
        var continuation: AsyncStream<Event>.Continuation!
        stream = AsyncStream { cont in
            continuation = cont
        }
        self.continuation = continuation
    }

    func emit(_ event: Event) {
        continuation.yield(event)
    }

    func finish() {
        continuation.finish()
    }
}

// Example: shared progress monitoring
func processItemsWithProgress(items: [Item]) async {
    let eventStream = EventStream()

    // Task 1: Process items
    Task {
        for item in items {
            await processItem(item)
            eventStream.emit(.itemProcessed(item.id))
        }
        eventStream.finish()
    }

    // Task 2: Update UI with progress
    Task { @MainActor in
        for await event in eventStream.stream {
            switch event {
            case .itemProcessed(let id):
                print("Item \(id) processed")
            }
        }
    }
}

enum Event {
    case itemProcessed(String)
}

ตัวเลือกสถาปัตยกรรม: Actor สำหรับสถานะแบบรวมศูนย์ที่มีตรรกะธุรกิจ AsyncStream สำหรับการสื่อสารแบบ event-driven ระหว่างคอมโพเนนต์ที่แยกออกจากกัน

พร้อมที่จะพิชิตการสัมภาษณ์ iOS แล้วหรือยังครับ?

ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ

การยกเลิก Task คืออะไรและจัดการอย่างไร?

คำตอบที่คาดหวัง: การยกเลิก Task ช่วยให้สามารถยกเลิกการดำเนินการอะซิงโครนัสที่กำลังดำเนินการอยู่ งานต้องตรวจสอบ Task.isCancelled เป็นระยะหรือใช้ Task.checkCancellation() ที่โยนข้อผิดพลาด

CancellableOperations.swiftswift
// Implementing proper task cancellation

struct ImageProcessor {
    // Cancellable processing with explicit checks
    func processImages(_ images: [UIImage]) async throws -> [ProcessedImage] {
        var results: [ProcessedImage] = []

        for (index, image) in images.enumerated() {
            // Check 1: Boolean check (continue or skip)
            if Task.isCancelled {
                print("Cancelled after \(index) images")
                break // Graceful stop
            }

            let processed = try await processImage(image)
            results.append(processed)

            // Check 2: Automatic throw if cancelled
            try Task.checkCancellation()
        }

        return results
    }

    private func processImage(_ image: UIImage) async throws -> ProcessedImage {
        // Simulate long processing
        for _ in 0..<10 {
            try await Task.sleep(nanoseconds: 100_000_000)

            // ✅ Check cancellation in long loops
            try Task.checkCancellation()
        }

        return ProcessedImage(from: image)
    }
}

// SwiftUI: Automatic cancellation when view disappears
struct ImageGalleryView: View {
    @State private var images: [ProcessedImage] = []

    var body: some View {
        ScrollView {
            // Display images
        }
        .task {
            // ✅ Task cancelled automatically when view disappears
            let processor = ImageProcessor()
            do {
                images = try await processor.processImages(sourceImages)
            } catch is CancellationError {
                print("Processing cancelled")
            }
        }
    }
}

// Manual cancellation of stored task
class DownloadViewModel {
    private var downloadTask: Task<Void, Never>?

    func startDownload() {
        downloadTask = Task {
            do {
                try await performLongDownload()
            } catch is CancellationError {
                print("Download cancelled by user")
            }
        }
    }

    func cancelDownload() {
        // Explicit cancellation of stored task
        downloadTask?.cancel()
        downloadTask = nil
    }

    private func performLongDownload() async throws {
        try Task.checkCancellation()
        // Download logic
    }
}

ประเด็นสำคัญ:

  • Task.isCancelled: การตรวจสอบที่ไม่บล็อก (คืนค่า bool)
  • Task.checkCancellation(): โยน CancellationError หากถูกยกเลิก
  • SwiftUI modifier .task { }: การยกเลิกอัตโนมัติเมื่อ view หายไป
การยกเลิกแบบร่วมมือ

Swift ใช้รูปแบบการยกเลิกแบบร่วมมือ: งานจะไม่ถูกฆ่าโดยบังคับ โค้ดต้องตรวจสอบ Task.isCancelled หรือ checkCancellation() อย่างแข็งขันเพื่อตอบสนองต่อการยกเลิก หากไม่มีการตรวจสอบเหล่านี้ งานจะดำเนินต่อไปอย่างไม่มีกำหนด

จะใช้ MainActor อย่างถูกต้องในแอปพลิเคชัน SwiftUI ได้อย่างไร?

คำตอบที่คาดหวัง: ใส่ annotation ViewModel ด้วย @MainActor เพื่อรับประกันว่าการอัปเดตสถานะ UI ทั้งหมดเกิดขึ้นบน main thread ใช้ @MainActor กับฟังก์ชันแต่ละรายการหากเฉพาะการดำเนินการบางอย่างเท่านั้นที่แตะ UI

MainActorPatterns.swiftswift
// Proper MainActor usage in SwiftUI architecture

// Pattern 1: Entire ViewModel @MainActor
@MainActor
class UserViewModel: ObservableObject {
    @Published var user: User?
    @Published var isLoading = false
    @Published var errorMessage: String?

    private let repository: UserRepository

    init(repository: UserRepository) {
        self.repository = repository
    }

    // ✅ All methods implicitly @MainActor
    func loadUser(id: Int) async {
        isLoading = true // No need for await or MainActor.run
        errorMessage = nil

        do {
            // Network call done on background thread by runtime
            user = try await repository.fetchUser(id: id)
        } catch {
            errorMessage = error.localizedDescription
        }

        isLoading = false // Always on MainActor
    }

    // Synchronous method also on MainActor
    func clearUser() {
        user = nil
        errorMessage = nil
    }
}

// Pattern 2: Selective methods with @MainActor
class DataSyncService {
    // ❌ Not @MainActor on class (no UI here)

    func syncData() async throws {
        // Background processing
        let data = try await fetchRemoteData()
        let processed = processData(data)

        // ✅ Switch to MainActor only for UI
        await updateUI(with: processed)
    }

    @MainActor
    private func updateUI(with data: ProcessedData) {
        // Update observable property
        NotificationCenter.default.post(
            name: .dataDidSync,
            object: data
        )
    }

    // Background work (not @MainActor)
    private func fetchRemoteData() async throws -> Data {
        // Network call
        Data()
    }

    private func processData(_ data: Data) -> ProcessedData {
        // CPU-intensive processing in background
        ProcessedData()
    }
}

// Pattern 3: Closure annotation
class ImageLoader {
    func loadImage(url: URL, completion: @MainActor @escaping (UIImage?) -> Void) async {
        let image = try? await downloadImage(from: url)

        // ✅ Completion guaranteed on MainActor
        await completion(image)
    }

    private func downloadImage(from url: URL) async throws -> UIImage {
        let (data, _) = try await URLSession.shared.data(from: url)
        return UIImage(data: data) ?? UIImage()
    }
}

ข้อผิดพลาดทั่วไป: การทำเครื่องหมายทั้งคลาสเป็น @MainActor เมื่อมีเพียงเมธอดบางอย่างเท่านั้นที่แตะ UI สิ่งนี้บังคับให้โค้ดทั้งหมดทำงานบน main thread รวมถึงการดำเนินการที่หนักหน่วงซึ่งควรอยู่ในเบื้องหลัง

จะจัดการ data races ด้วย Sendable ได้อย่างไร?

คำตอบที่คาดหวัง: โปรโตคอล Sendable รับประกันว่าประเภทสามารถแชร์ระหว่างงานได้โดยไม่มีความเสี่ยงต่อ data race ประเภทค่า (struct, enum) เป็น Sendable โดยอัตโนมัติ คลาสต้องเป็น final ที่มีคุณสมบัติที่ไม่เปลี่ยนแปลงหรือได้รับการป้องกัน

SendableCompliance.swiftswift
// Making types safe for concurrent access

// ✅ Struct: automatically Sendable (value type)
struct UserData: Sendable {
    let id: Int
    let name: String
    let email: String
}

// ✅ Enum: automatically Sendable
enum LoadingState: Sendable {
    case idle
    case loading
    case loaded(UserData)
    case failed(Error) // ⚠️ Error must also be Sendable
}

// ❌ Class with mutable state: not Sendable by default
class UnsafeCounter {
    var count = 0 // Mutable, unprotected

    func increment() {
        count += 1 // Data race possible
    }
}

// ✅ Immutable class: explicit Sendable
final class SafeConfig: @unchecked Sendable {
    let apiKey: String
    let timeout: TimeInterval

    init(apiKey: String, timeout: TimeInterval) {
        self.apiKey = apiKey
        self.timeout = timeout
    }
}

// ✅ Class with actor-protected state
actor SafeCounter: Sendable {
    private var count = 0 // Protected by actor isolation

    func increment() {
        count += 1 // Thread-safe automatically
    }

    func getValue() -> Int {
        return count
    }
}

// ✅ Class with manually protected state
final class ThreadSafeCache: @unchecked Sendable {
    private let lock = NSLock()
    private var storage: [String: Data] = [:]

    func store(_ data: Data, for key: String) {
        lock.lock()
        defer { lock.unlock() }
        storage[key] = data
    }

    func retrieve(for key: String) -> Data? {
        lock.lock()
        defer { lock.unlock() }
        return storage[key]
    }
}

// Usage: compiler checks Sendable
func processInBackground(data: UserData) { // ✅ UserData is Sendable
    Task.detached {
        // No warning: UserData is Sendable value type
        print("Processing user: \(data.name)")
    }
}

func processUnsafe(counter: UnsafeCounter) {
    Task.detached {
        // ⚠️ Warning: UnsafeCounter is not Sendable
        // counter.increment()
    }
}

กฎ Sendable:

  • Struct/Enum ที่มีคุณสมบัติ Sendable: เป็น Sendable โดยอัตโนมัติ
  • คลาส: ต้องเป็น final + ไม่เปลี่ยนแปลง หรือใช้ @unchecked Sendable พร้อมการป้องกันแบบแมนนวล (lock, actor)
  • Closure: เป็น Sendable โดยอัตโนมัติหากจับเฉพาะประเภท Sendable
@unchecked Sendable

@unchecked Sendable ปิดการใช้งานการตรวจสอบของคอมไพเลอร์ ใช้เฉพาะเมื่อมีการรับประกัน thread-safety ด้วยตนเอง (lock, serial queue) เป็นความรับผิดชอบของนักพัฒนาในการหลีกเลี่ยง data races

พร้อมที่จะพิชิตการสัมภาษณ์ iOS แล้วหรือยังครับ?

ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ

บทสรุป

การเชี่ยวชาญ Swift Structured Concurrency กลายเป็นสิ่งจำเป็นสำหรับการสัมภาษณ์ iOS ในปี 2026 ผู้สรรหาทดสอบสามระดับ: ความเข้าใจในแนวคิด (async/await กับ callback), ความเชี่ยวชาญในรูปแบบ (TaskGroup, actor isolation) และการดีบัก (การยกเลิก, Sendable)

รายการตรวจสอบการเตรียมตัว:

  • ✅ อธิบาย async/await กับ DispatchQueue ด้วยตัวอย่างที่เป็นรูปธรรม
  • ✅ แสดงการใช้ TaskGroup สำหรับการดำเนินการแบบขนาน
  • ✅ ใช้งาน actor ที่ปลอดภัยต่อ thread เพื่อปกป้องสถานะที่เปลี่ยนแปลงได้
  • ✅ จัดการข้อผิดพลาดในบริบทที่เกิดขึ้นพร้อมกัน (Result, throwing)
  • ✅ แยกแยะ Task, Task.detached และ async let พร้อมกรณีการใช้งาน
  • ✅ ใช้งาน timeout บนการดำเนินการอะซิงโครนัส
  • ✅ ใช้ MainActor อย่างถูกต้องในสถาปัตยกรรม SwiftUI
  • ✅ เข้าใจ Sendable และหลีกเลี่ยง data races

ผู้สมัครที่ดีที่สุดผสมผสานทฤษฎีและการปฏิบัติ: อธิบาย "ทำไม" (หลีกเลี่ยง data races, ปรับปรุงการอ่าน) และ "อย่างไร" (โค้ดที่ทำงานได้พร้อมการจัดการข้อผิดพลาด) ฝึกฝนกับโครงการจริงเพื่อรวบรวมรูปแบบเหล่านี้

เริ่มฝึกซ้อมเลย!

ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ

แท็ก

#swift
#concurrency
#async-await
#actors
#interview

แชร์

บทความที่เกี่ยวข้อง

การย้ายจาก Combine ไปยัง async/await ใน Swift พร้อมรูปแบบการอยู่ร่วมกัน

Combine vs async/await ใน Swift: รูปแบบการย้ายระบบแบบค่อยเป็นค่อยไป

คู่มือฉบับสมบูรณ์สำหรับการย้ายจาก Combine ไปยัง async/await ใน Swift: กลยุทธ์แบบค่อยเป็นค่อยไป รูปแบบการเชื่อมโยง และการอยู่ร่วมกันของกระบวนทัศน์ในโค้ดเบส iOS

สถาปัตยกรรมการสมัครสมาชิก iOS StoreKit 2 และการตรวจสอบใบเสร็จ

การสัมภาษณ์ StoreKit 2: การจัดการการสมัครสมาชิกและการตรวจสอบใบเสร็จ

เชี่ยวชาญคำถามสัมภาษณ์ iOS เกี่ยวกับ StoreKit 2 การจัดการการสมัครสมาชิก การตรวจสอบใบเสร็จ และการนำการซื้อในแอปไปใช้ พร้อมตัวอย่างโค้ด Swift ที่ใช้งานได้จริง

Swift Testing Framework พร้อมมาโคร #expect และ #require สำหรับการสัมภาษณ์ iOS

Swift Testing Framework สัมภาษณ์ 2026: มาโคร #expect และ #require เทียบกับ XCTest

เรียนรู้ Swift Testing Framework ใหม่สำหรับการสัมภาษณ์ iOS: มาโคร #expect และ #require การย้ายจาก XCTest แพทเทิร์นขั้นสูงและข้อผิดพลาดที่พบบ่อย