33. Memory leak with a closure
This view controller is never deallocated after it is dismissed. Why?
class ProfileVC: UIViewController {
var onUpdate: (() -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
onUpdate = {
self.reload()
}
}
func reload() {}
}- A.viewDidLoad is called twice
- B.Optionals always leak memory in Swift
- C.reload is not marked @objc
- D.The VC strongly holds the closure and the closure strongly captures self, creating a retain cycle; use [weak self]
Show answer
Answer: D. The VC strongly holds the closure and the closure strongly captures self, creating a retain cycle; use [weak self]
Swift uses ARC. The controller owns onUpdate, and the closure captures self strongly, so each keeps the other alive and the reference count never reaches zero. Capturing [weak self] (or unowned when lifetime is guaranteed) breaks the cycle. Xcode Memory Graph Debugger can show such cycles.
34. Task vs Task.detached
Inside a @MainActor view model method, what is the difference between starting work with Task { } and with Task.detached { }?
- A.Task { } inherits the current actor (here the main actor), priority and task-local values; Task.detached inherits none of them
- B.They are the same, but detached tasks are faster
- C.Task.detached always runs on the main thread
- D.Task { } is a child of the calling method and is cancelled when that method returns, while a detached task keeps running on its own
Show answer
Answer: A. Task { } inherits the current actor (here the main actor), priority and task-local values; Task.detached inherits none of them
A plain Task is unstructured but takes its context from where it is created, so in a @MainActor method its body runs on the main actor. Task.detached starts fresh with no actor, priority or task-locals. Neither is cancelled when the method returns; you must keep a handle and cancel it yourself.
35. unowned self in a network callback
The user closes this screen before the request finishes. What happens when the completion handler runs?
final class OrderViewController: UIViewController {
func load() {
api.fetchOrder { [unowned self] result in
self.render(result)
}
}
}- A.The closure is skipped automatically
- B.The app crashes, because unowned assumes self is still alive; use [weak self] with guard let self else { return }
- C.self is kept alive until the closure finishes, because unowned captures still hold a strong reference while a closure is pending
- D.It does not compile, because unowned cannot be used in closures
Show answer
Answer: B. The app crashes, because unowned assumes self is still alive; use [weak self] with guard let self else { return }
unowned does not keep the object alive and does not become nil, so accessing a deallocated object traps at runtime. It is only safe when the closure can never outlive self. For network callbacks the lifetime is unknown, so [weak self] plus a guard is the safe choice.
36. When a Swift array is copied
When is the array storage actually copied, and what does this code print?
var a = [1, 2, 3]
var b = a // (1)
b.append(4) // (2)
print(a.count, b.count)
- A.At (1), because assigning an array to a new variable always copies every element right away; it prints 3 4
- B.At (2), because b is mutated while it still shares storage with a; it prints 3 4
- C.Never; it prints 4 4
- D.At (2); it prints 4 4
Show answer
Answer: B. At (2), because b is mutated while it still shares storage with a; it prints 3 4
Swift arrays are value types that use copy-on-write. Assignment only shares the buffer; the real copy happens on the first mutation when the buffer is not uniquely referenced. The result still behaves like a copy, so a has 3 items and b has 4.
37. Closure capture list
What does this code print?
var count = 1
let a = { print(count) }
let b = { [count] in print(count) }
count = 2
a()
b()- A.1 then 1
- B.2 then 2
- C.2 then 1
- D.1 then 2
Show answer
Answer: C. 2 then 1
Closures capture variables by reference by default, so a sees the updated count of 2. A capture list [count] copies the value when the closure is created, so b keeps 1.
38. imageURL is always nil
The JSON has "image_url". The struct property is imageURL: String?, and the decoder uses .convertFromSnakeCase. Why is imageURL always nil?
- A.The key becomes "imageUrl", which is not imageURL
- B.convertFromSnakeCase only works for Int fields
- C.Optional properties are never decoded at all
- D.The decoder also needs .useDefaultKeys set
Show answer
Answer: A. The key becomes "imageUrl", which is not imageURL
convertFromSnakeCase turns image_url into imageUrl, capitalising only the first letter after each underscore. imageURL does not match, and because it is optional, decoding silently gives nil. Rename to imageUrl or add CodingKeys.
39. Date decoding fails on milliseconds
The server sends "2026-10-16T10:30:00.123Z". JSONDecoder uses .iso8601 and throws a dataCorrupted error. Why?
- A.JSONDecoder cannot decode any Date values
- B.The Z suffix is invalid in ISO 8601
- C..iso8601 does not accept fractional seconds
- D.Dates must be sent as Unix timestamps
Show answer
Answer: C. .iso8601 does not accept fractional seconds
The built-in .iso8601 strategy expects no fractional seconds. Use .custom with an ISO8601DateFormatter that includes .withFractionalSeconds, or a Date.ISO8601FormatStyle that includes them, or ask the API for a consistent format.
40. Passing a class into a Task
In Swift 6 language mode, sending a mutable non-Sendable class instance from the main actor into a detached Task gives an error. Why?
- A.Both sides could mutate it at once, a data race
- B.Classes cannot be used inside any task at all
- C.Detached tasks only accept String values
- D.The class must first be marked with @objc
Show answer
Answer: A. Both sides could mutate it at once, a data race
Values crossing isolation boundaries must be Sendable, or proven safe to transfer. A mutable class shared by two concurrent contexts could race. Fixes include making it a struct, an actor, immutable and final Sendable, or not sharing it after sending.
41. Global cache in Swift 6
After switching to the Swift 6 language mode, this line reports that the variable is not concurrency-safe. What is a proper fix?
var imageCache: [URL: Data] = [:]
- A.Mark it lazy so it is created later
- B.Wrap every use in DispatchQueue.main.async
- C.Change it to an implicitly unwrapped optional
- D.Isolate it to @MainActor or move it into an actor
Show answer
Answer: D. Isolate it to @MainActor or move it into an actor
Global and static mutable variables are shared state that any thread could touch. Swift 6 rejects them unless they are isolated to a global actor, protected inside an actor, or made immutable let constants of Sendable type.
42. Using nonisolated in an actor
An actor has let id: UUID and a computed property label built only from id. Why mark label nonisolated?
- A.It makes label always run on the main thread
- B.It allows label to change mutable actor state
- C.It reads no mutable state, so no await is needed
- D.It quietly turns the actor into a class
Show answer
Answer: C. It reads no mutable state, so no await is needed
nonisolated members are not protected by the actor, so they cannot read mutable isolated state. Computing from immutable Sendable lets is safe, and making it nonisolated allows synchronous calls, for example to satisfy a protocol like Hashable.
43. Bridging a callback with a continuation
A callback API sometimes calls its completion twice. It is wrapped with withCheckedThrowingContinuation. What happens on the second resume?
- A.The second value replaces the first one
- B.A runtime trap: resume must happen exactly once
- C.The second call is ignored silently
- D.The awaiting task returns two times
Show answer
Answer: B. A runtime trap: resume must happen exactly once
Checked continuations detect misuse: resuming twice traps, and never resuming logs a leak and leaves the caller suspended forever. Guard against double callbacks, for example with a flag, before resuming.
44. Blocking inside an async function
An async function calls DispatchSemaphore.wait() to wait for another async task to signal. Under load the app hangs. Why?
- A.Semaphores only work when used on the main thread
- B.async functions cannot call any Dispatch API
- C.wait() always returns at once inside async code
- D.It blocks a pool thread that other tasks need
Show answer
Answer: D. It blocks a pool thread that other tasks need
Swift concurrency runs tasks on a small cooperative thread pool and assumes tasks never block. A blocked thread cannot run the task that would signal, which can deadlock. Use await on the other task, or async primitives like continuations.
45. How if works in a SwiftUI body
Why can you write plain if/else and several views in a row inside a SwiftUI body without return or arrays?
- A.Swift ignores every expression except the last one
- B.SwiftUI parses the body as a string at runtime
- C.@ViewBuilder, a result builder, combines them into one view
- D.Each line in the body creates a separate window
Show answer
Answer: C. @ViewBuilder, a result builder, combines them into one view
Result builders transform a block of statements into calls such as buildBlock and buildEither. @ViewBuilder uses this to combine child views and conditionals into one typed view, such as _ConditionalContent.
46. Wrong photo in a reused cell
In a UITableView, each cell starts an image download in cellForRowAt. While scrolling fast, some rows show another row’s image. What fixes it?
- A.Give every row its own unique reuse identifier
- B.Call reloadData() after every image finishes
- C.Download all the images on the main thread
- D.Reset and cancel in prepareForReuse; check the row
Show answer
Answer: D. Reset and cancel in prepareForReuse; check the row
Cells are reused, so a slow download from an earlier row can finish after the cell shows a new row. Reset the image and cancel the task in prepareForReuse, and verify the cell still represents the same item before setting the image.
47. Universal links open Safari
Universal links for example.com open Safari instead of the app. The apple-app-site-association file is reachable only through a redirect to www.example.com. What is wrong?
- A.Universal links only ever work for www subdomains
- B.The file must be renamed to aasa.txt to be found
- C.Universal links need the app to be open already
- D.The AASA file must be served over HTTPS with no redirect
Show answer
Answer: D. The AASA file must be served over HTTPS with no redirect
iOS fetches /.well-known/apple-app-site-association for each associated domain over HTTPS and does not follow redirects. Serve it directly with the right appIDs, and list each domain in the Associated Domains entitlement.
48. Importing 50,000 rows in SwiftData
An import of 50,000 records freezes the UI because it uses the main view’s ModelContext. What is the recommended approach?
- A.Pass the main ModelContext into a detached Task
- B.Insert rows on the main thread in smaller views
- C.Do the import in a @ModelActor with its own context
- D.Use UserDefaults for the import instead
Show answer
Answer: C. Do the import in a @ModelActor with its own context
ModelContext is not Sendable and the main context is tied to the main actor. A @ModelActor creates its own context from the ModelContainer on a background executor; saved changes then reach the main context.