Last active
April 3, 2021 16:03
-
-
Save nikitamounier/0e754f4d1e430952d7f010565cc279e9 to your computer and use it in GitHub Desktop.
Protocol to easily implement copy-on-write behaviour for your large value types.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
protocol Copyable { | |
func copy() -> Self | |
} | |
/// A protocol for value types which gives them copy-on-write behaviour. This means that when multiple variables are pointing to the type, they point to the same underlying data to avoid excessive copying. When one of them modifies the type, it copies the type, therefore keeping value semantics. | |
@dynamicMemberLookup | |
protocol CopyOnWrite { | |
associatedtype Storage: AnyObject & Copyable | |
/// The underlying storage of the type, which is a reference type. | |
var _storage: Storage { get set } | |
} | |
extension CopyOnWrite { | |
subscript<Value>(dynamicMember keypath: ReferenceWritableKeyPath<Storage, Value>) -> Value { | |
get { | |
return _storage[keyPath: keypath] | |
} | |
set { | |
if !isKnownUniquelyReferenced(&_storage) { | |
_storage = _storage.copy() | |
} | |
self._storage[keyPath: keypath] = newValue | |
} | |
} | |
} | |
struct LargeType: CopyOnWrite { | |
typealias Storage = _Storage | |
var _storage: _Storage | |
init(value: String) { | |
self._storage = _Storage(value: value) | |
} | |
final class _Storage: Copyable { // requires `copy()` method | |
var value: String | |
init(value: String) { | |
self.value = value | |
} | |
func copy() -> _Storage { | |
return .init(value: self.value) | |
} | |
} | |
} | |
var first = LargeType(value: "first") | |
print(first.value) // first | |
var second = first | |
print(second.value) // first | |
print(first._storage === second._storage) // true | |
second.value = "second" | |
print(first.value) // first | |
print(second.value) // second | |
print(first._storage === second._storage) // false |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment