With the release of Swift 5.5, the ownership model and move semantics were integrated into the language, enhancing control over data ownership and allowing the compiler to optimize moves, reducing the number of copies, which is crucial for high-performance scenarios.
Move semantics implies that when passing a value (for example, a struct), you can "transfer" ownership of that value without copying. The compiler can invalidate the original variable (similar to move in C++). Currently, the ownership model and move semantics are more implemented as an experiment (actor isolation, sendable types, @_move, consuming/self-consuming) and are expected to appear in the public API.
The main difference from ARC is that move semantics applies to value types, while ARC manages the lifetime of reference objects.
Example (ownership semantics, Swift 5.5+):
func consume<T>(_ x: __owned T) { /* ... */ } struct LargeArray { var storage: [Int] mutating func clear() { storage.removeAll() } consuming func consumeSelf() { // self is not accessible after the call } }
Ownership management helps avoid unexpected copies when working with large structures.
Nuances:
What is the difference between passing a struct object to a function by value, by reference, and by move semantics in Swift?
Answer:
Example:
func foo(_ x: MyStruct) { /* copy */ } func bar(_ x: inout MyStruct) { /* by reference */ } func baz(_ x: __owned MyStruct) { /* move semantics, does not copy */ }
Story
In the project, when passing large structures through functions, there was always implicit copying occurring, increasing memory overhead. After implementing experimental move semantics and more thoughtful ownership management, it was possible to redistribute the load cleanly and speed up critical sections.
Story
Many developers incorrectly used inout, believing it implemented move semantics, while the value remained accessible, leading to ambiguous ownership of the variable, resulting in bugs and logic violations.
Story
Data management error between threads: lack of the Sendable qualifier on structures, which led to unexpected copying or ownership errors when asynchronously working with large structures via actor or Task.