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.