Skip to Content
Course content

56: Common Swift Interview Questions on Value Semantics

Click on the "Edit" button in the top corner of the screen to edit your slide content.

If you're heading into a Swift interview, you can bet your last dollar that value semantics will come up. Interviewers love this topic because it separates the people who just know the syntax from the people who actually understand how Swift manages memory and state. Let's walk through the questions that usually trip people up.

Wait, isn't "value semantics" just another way of saying "use a struct"?

Not exactly. While structs are the primary way we achieve value semantics in Swift, the "semantics" part is about the behavior. When you say a type has value semantics, you're promising that assigning it to a new variable creates a completely independent copy. If I change the copy, the original stays exactly as it was.

I've seen candidates lose points here because they focus on the keyword struct rather than the concept of independence. Look at this example with a ShoppingCart. If this were a class, adding an item to a "backup" cart would accidentally add it to the live cart too. With value semantics, that's impossible.

struct ShoppingCart {
    var items: [String]
}

var liveCart = ShoppingCart(items: ["MacBook"])
var backupCart = liveCart // Value semantics: a copy is made

backupCart.items.append("Magic Mouse")

print(liveCart.items)  // ["MacBook"] - Still safe!
print(backupCart.items) // ["MacBook", "Magic Mouse"]

The key takeaway is predictability. You don't have to worry about some other part of your app mutating your data behind your back. That's the real win.

If structs copy everything, why aren't my huge arrays slowing down my app?

This is the "gotcha" question. If you tell an interviewer that Swift copies every single element of an array every time you pass it to a function, they'll know you haven't dealt with performance profiling yet. Swift uses a technique called Copy-on-Write (CoW).

Essentially, Swift is lazy. When you assign one array to another, they both actually point to the same memory buffer in the background. Swift only performs the actual copy the very first time one of those variables is mutated. I like to think of it as a "shared lease" that only becomes a "private purchase" once you decide to paint the walls.

This is why you can pass a 10,000-element array into a function without a performance hit—as long as that function only reads the data, no copying ever happens.

Can I actually implement Copy-on-Write for my own custom types?

Yes, you can, but you have to do a bit of manual lifting. Since CoW is a feature of the Standard Library's collections, you have to build the mechanism yourself using a private class to hold the actual data. The struct acts as the "manager" that checks if the data is shared before mutating it.

Here is how I usually implement it when I'm building a custom data buffer or a heavy wrapper:

final class Storage {
    var data: [Int]
    init(data: [Int]) { self.data = data }
}

struct SmartBuffer {
    private var storage: Storage

    init(data: [Int]) {
        self.storage = Storage(data: data)
    }

    var data: [Int] {
        get { storage.data }
        set {
            // This is the magic: check if more than one variable owns this storage
            if !isKnownUniquelyReferenced(&storage) {
                storage = Storage(data: newValue) // Copy the data
            } else {
                storage.data = newValue // Mutate in place
            }
        }
    }
}

The isKnownUniquelyReferenced function is the secret sauce here. It checks the reference count of the class instance. If the count is higher than one, it means another variable is holding onto that data, and it's time to make a unique copy before we change anything.

When would I actually choose a class over a struct in a real project?

In an interview, the "correct" answer is often "use structs by default." But in the real world, you need reference semantics (classes) when you need a single source of truth that multiple parts of the app must observe and update simultaneously.

Think of a DatabaseManager or a UserSession. You don't want ten different copies of the user's login state floating around your app; you want one object that everyone references. If you used a struct for a UserSession, you'd find yourself passing that struct back and forth through every single initializer in your app just to keep the state synced, which is a nightmare to maintain.




📋 Practical Task

Implementing a Copy-on-Write Buffer for Audio Samples

You are building a high-performance audio processing app. You've noticed that passing large buffers of audio samples (represented as [Float]) as structs is causing unexpected memory spikes because you're accidentally triggering copies during mutation.

Your Task: Create a struct called AudioBuffer that implements the Copy-on-Write pattern. It should:

  • Use a private internal class (e.g., BufferStorage) to hold the [Float] data.
  • Implement a property samples that allows reading and writing.
  • Use isKnownUniquelyReferenced inside the setter of samples to ensure that the internal storage is only copied when it is shared by more than one AudioBuffer instance.

Test your implementation by creating two buffers, assigning one to the other, and verifying that mutating the second one does not affect the first.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.