Edit: the "stack vs heap" I mentioned in the following is actually "value semantics vs "reference semantics".
class
vs. struct
In Swift, we use the keyword class
to indicate the instance of this class
type we define is allocated on the heap. We use the keyword struct
to define an object (in this post, I extend the meaning of the term object) which is allocated on the stack.
It seems to be a concise solution for the Swift memory model. Programmers don’t have to deal with pointers explicitly, and everything works well. But I believe that the keyword class
actually bring more drawbacks than benefits. I’d like to show the drawbacks of class
as the following.
First, class
allows mutation by default. For example, the first code snippets shown as follows is valid, and the second one is invalid:
class MyClass {
var x: Int = 1
}
let instance1 = MyClass()
instance1.x = 2
struct MyStruct {
var x: Int = 1
}
let instance2 = MyStruct()
instance2.x = 2 // ERROR: Cannot assign to property: 'instance2' is a 'let' constant
It means that even if we declare instance1
with let
, we can still modify it. If we use class
to define our abstract data structure, we give up the compiler’s protection of mutability. There are no way to prevent users to mutate instance1
.
Moreover, struct
requires you to add the keyword mutating
to the methods that can modify the data structure. However, you can mutate a class
through any methods without any additional marks. We know that an immutable data/object is almost always better than a mutable one from the perspective of the software engineering and optimization. The class
keyword doesn’t play well with it.
Second, the definition of class
requires us to decide whether the object should be allocated on the heap or the stack when defining it instead of using it. But in many situations, we want to postpone this decision until we actually use it. What’s worse, it’s actually not convenient to refactor a class
to struct
, since struct
has some additional features that class
doesn’t have (e.g., automatic member-wise constructor generation, mutating
keyword…). We may also want two instances with the same structures, but one on the stack and the other on the heap. In this case, we have to write two data structures.
Solution?
Maybe we should use a Rust-like strategy, i.e., using an Arc<T>
container to indicate that an object is allocated on the heap, and using Weak<T>
to indicate a heap-allocated variable marked as weak
. I create a prototype to mimic it: AtomicReferenceCell(It doesn't work actually...)
Of course, in UIKit/AppKit there are many classes, so if we write code for Apple’s platforms, we cannot avoid to use class
. But in other situations, we may need to consider another to use struct
.