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

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
// 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 สำหรับจำนวนงานขนานแบบไดนามิกพร้อมการรวบรวมผลลัพธ์
// 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) เพื่อรักษาความปลอดภัยของการเข้าถึงพร้อมกัน
// 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 เป็น actor ระดับโลกสำหรับการดำเนินการ UI การทำเครื่องหมายคลาสด้วย @MainActor บังคับให้เมธอดทั้งหมดของมันทำงานบน main thread ระวังการเรียกที่บล็อกซึ่งอาจทำให้ส่วนติดต่อค้าง
พร้อมที่จะพิชิตการสัมภาษณ์ iOS แล้วหรือยังครับ?
ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ
จะจัดการข้อผิดพลาดใน TaskGroup ได้อย่างไร?
คำตอบที่คาดหวัง: withThrowingTaskGroup ส่งต่อข้อผิดพลาดแรกที่พบและยกเลิกงานที่เหลืออัตโนมัติ ในการรวบรวมข้อผิดพลาดทั้งหมด ใช้ Result ใน TaskGroup
// 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
// 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
// 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 เพื่อสื่อสารระหว่างงานผ่านสตรีมของค่า
// 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() ที่โยนข้อผิดพลาด
// 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
// 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 ที่มีคุณสมบัติที่ไม่เปลี่ยนแปลงหรือได้รับการป้องกัน
// 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 ปิดการใช้งานการตรวจสอบของคอมไพเลอร์ ใช้เฉพาะเมื่อมีการรับประกัน 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, ปรับปรุงการอ่าน) และ "อย่างไร" (โค้ดที่ทำงานได้พร้อมการจัดการข้อผิดพลาด) ฝึกฝนกับโครงการจริงเพื่อรวบรวมรูปแบบเหล่านี้
เริ่มฝึกซ้อมเลย!
ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ
แท็ก
แชร์
บทความที่เกี่ยวข้อง

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

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

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