An actor caches a computed value: `func total() async -> Int { if let c = cache { return c }; let v = await recompute(); cache = v; return v }`. Under concurrent callers, what correctness problem exists?
- A. None — actor isolation serializes all access, so `cache` can never be read stale
- B. During the `await recompute()` suspension another task can enter the actor and also see `cache == nil`, causing redundant recomputes and a possible last-writer-wins overwrite ✓
- C. The compiler rejects it because `cache` is mutated inside an async method on an actor
- D. `recompute()` runs on a background thread, so `cache = v` is a data race the runtime will trap on
Correct answer: B. Actors are reentrant: at an `await` suspension point another task can run actor code, so the pre-await `cache == nil` check no longer holds and the invariant breaks.
A `struct Container { var items: [Int] }` uses copy-on-write via `Array`. You hold `var a = Container(...)`, do `let b = a`, then `a.items.append(1)`. What determines whether the append copies the backing buffer?
- A. It always copies because `let b = a` copies the whole struct including the array buffer eagerly
- B. It copies only if the `Array`'s storage is not uniquely referenced at mutation time — since `b` shares the buffer, `isKnownUniquelyReferenced` is false and it copies ✓
- C. It never copies because `a` and `b` are separate struct instances with independent buffers
- D. It copies only if `Container` is marked `@frozen`, otherwise the buffer is shared forever
Correct answer: B. COW defers duplication until mutation and copies only when the buffer's reference count shows it is shared, which it is because `b` still points at the same storage.
In SwiftUI, a parent view creates a child as `ChildView(model: MyModel())` where `ChildView` declares `@StateObject private var model`. The parent re-renders frequently. What happens to the `MyModel` instance?
- A. A fresh `MyModel` is created and adopted on every parent re-render, resetting the child's state
- B. The `MyModel()` in the initializer is evaluated on every re-render but `@StateObject` keeps the first instance and discards the newly created ones ✓
- C. `@StateObject` forbids passing an instance through the initializer; this fails to compile
- D. The child observes the parent's model by reference, so parent re-renders mutate it in place
Correct answer: B. `@StateObject`'s autoclosure runs each time the initializer is called but SwiftUI only uses the value from first appearance and keeps that instance stable, so the extra allocations are wasted but state is preserved.
Swift 6 flags: `final class Cache { var data: [String:Int] = [:] }` passed into `Task.detached { cache.data["x"] = 1 }`. Why the data-race diagnostic, and the correct minimal fix?
- A. `Cache` isn't `Sendable` because it has mutable non-isolated state; make it an `actor` (or add a lock and mark it `@unchecked Sendable`) ✓
- B. Add `@Sendable` to the closure — that alone makes the capture safe
- C. Mark `Cache` as `final` — it already is, so the error is a compiler bug you silence with `nonisolated`
- D. Change `var data` to `let data` — an immutable dictionary reference makes concurrent writes safe
Correct answer: A. A final class with mutable state is not `Sendable`; making it an actor gives isolated serialized access, whereas `@Sendable` on the closure or `let` on the reference does not protect the dictionary's contents from concurrent writes.
Given `protocol P { func f() -> String }` with `extension P { func f() -> String { "ext" }; func g() -> String { "g-" + f() } }` and `struct S: P { func f() -> String { "S" } }`. What does `(S() as P).g()` return?
- A. "g-ext" because `g` is only in the extension and calls the extension's `f`
- B. "g-S" because `f` is a protocol requirement, so it dispatches through the witness table to `S.f` ✓
- C. "g-ext" because calling through the existential `as P` erases the concrete type and uses defaults
- D. It is ambiguous and fails to compile due to two candidate `f` implementations
Correct answer: B. Because `f()` is declared in the protocol it is a requirement dispatched dynamically via the witness table to `S`'s override, so `g` (even from the extension) sees "S".
You register `NotificationCenter.default.addObserver(forName:object:queue:using:)` with a closure capturing `self`, in a UIViewController. What is the leak/behavior risk and best remedy?
- A. No risk — NotificationCenter uses weak references to observers automatically
- B. The block-based API retains the closure (and thus `self`); use `[weak self]` and/or remove the observer token in `deinit` ✓
- C. The controller leaks only if the notification never fires; firing it releases the closure
- D. Using `queue: .main` breaks the retain cycle by hopping threads
Correct answer: B. The block-based observer API strongly retains the closure until you remove the returned token, so capturing `self` strongly keeps the controller alive; capture weakly and remove the token.
A `Timer.scheduledTimer(withTimeInterval:repeats:true)` block captures `self` (a view controller). Even after the VC is dismissed it never deinits. Why, and which fix actually works?
- A. The run loop weakly holds the timer; add `[weak self]` and it will deinit immediately on dismiss
- B. The run loop retains the repeating timer, and the timer retains the closure retaining `self`; you must invalidate the timer (weak self alone won't stop the timer or free it) ✓
- C. `Timer` is `Sendable`, so it must be recreated on the main actor to break the cycle
- D. Setting `timer.tolerance` releases the closure between fires, breaking the retain cycle
Correct answer: B. The run loop keeps the repeating timer alive and the timer strongly holds its closure; `[weak self]` avoids the VC being kept alive but you still must `invalidate()` to stop and release the timer.
Comparing `func makeShape() -> some Shape` (opaque) vs `func makeShape() -> any Shape` (existential) for a factory returning one concrete `Circle`. Which statement is correct?
- A. `some Shape` erases the type at runtime and boxes it, while `any Shape` preserves the static type with no allocation
- B. `some Shape` pins one hidden concrete type known to the compiler (enabling static dispatch/specialization), while `any Shape` is a runtime box allowing heterogeneous returns at a dynamic-dispatch cost ✓
- C. They are interchangeable; `some` and `any` compile to identical code for a single concrete return
- D. `any Shape` cannot be returned from a function that has only one concrete return type
Correct answer: B. `some` guarantees one specific underlying type the compiler tracks (permitting specialization), whereas `any` is an existential box that supports differing dynamic types at the cost of indirection.
Two serial queues A and B. Code on A does `B.sync { ... }` while code on B does `A.sync { ... }` concurrently. What is the outcome, and what is the classic single-queue variant of this bug?
- A. A priority inversion that Instruments flags as a Hitch; the single-queue variant is calling `async` on the same queue
- B. A deadlock from cyclic `sync` waits; the single-queue variant is calling `queue.sync` from a block already running on that same serial queue ✓
- C. A livelock that resolves once the queues drain; the single-queue variant is nesting `autoreleasepool`
- D. Nothing — `DispatchQueue.sync` is reentrant on serial queues, so both complete
Correct answer: B. Mutual `sync` waits create a cyclic dependency deadlock, and the well-known single-queue form is `sync`-ing onto the very serial queue you're already executing on.
You call `context.perform` correctly for a Core Data background `NSManagedObjectContext`, fetch objects, then pass those `NSManagedObject` instances to the main thread to display. What rule is violated?
- A. None — objects fetched inside `perform` are thread-safe to read anywhere afterward
- B. Managed objects are bound to their context's queue; passing the objects across threads violates the threading contract — pass `NSManagedObjectID` and re-fetch on the main context instead ✓
- C. You must call `context.reset()` before crossing threads, otherwise faults leak
- D. Only saving across threads is unsafe; reading properties of a managed object from any thread is allowed
Correct answer: B. Managed objects are not thread-safe and belong to their context's queue; the safe hand-off is the `objectID`, re-fetched (or `object(with:)`) on the destination context.