В описании расширений сказано, что нельзя добавлять свойства хранения, но что если это не так и такое делать на самом деле можно?
Этот функционал перекочевал в Swift из Objective-C, он позволяет связывать объекты во время выполнения при помощи двух методов:
func objc_setAssociatedObject(_ object: Any,
_ key: UnsafeRawPointer,
_ value: Any?,
_ policy:objc_AssociationPolicy)
func objc_getAssociatedObject(_ object: Any, _ key: UnsafeRawPointer) -> Any
Где:
object - исходных объект, указывающий на другой объект
key - ключ для связи объектов
value - объект, который хотим привязать
policy - enum определяющий тип ссылки(OBJC_ASSOCIATION_ASSIGN, OBJC_ASSOCIATION_RETAIN_NONATOMIC,
OBJC_ASSOCIATION_COPY_NONATOMIC,
OBJC_ASSOCIATION_RETAIN,
OBJC_ASSOCIATION_COPY)
Так же есть и функционал удаляющий все связные объекты:
func objc_removeAssociatedObjects(_ object: Any)
К чему это все?
Как я уже говорил ранее Swift является безопасным для типов, поэтому запрещено использовать свойства хранения внутри расширений, так как это изменит размер экземпляра этого типа. Конечно, есть хитрый, но не самый изящный “Хак” как обойти эту систему, для этого нужно завести struct, в которой будем хранить все новые свойства, а заполнять их через get и set, как показано ниже, но будем честны выглядит это не очень.
class MyClass {}
extension MyClass {
struct ExtensionMyClass {
static var x: Int?
}
var x: Int? {
get {
ExtensionMyClass.x
}
set {
ExtensionMyClass.x = newValue
}
}
}
Тут то к нам на помощь и проходит associated objects. Используя вышеописанные методы, мы делаем почти тоже самое, что и в случае структуры.
import ObjectiveC
private var myKey: Void?
class SomeClass {}
extension SomeClass {
var name: String? {
get {
objc_getAssociatedObject(self, &myKey) as? String
}
set {
objc_setAssociatedObject(self,
&myKey,
newValue,
.OBJC_ASSOCIATION_RETAIN)
}
}
}
let a = SomeClass()
a.name = "2222"
print(a.name) //2222
Конечно же вместо String может быть любой тип, а использовать эти методы можно не только в таком случае, всему своя фантазия, но нужно быть очень осторожным с таким инструментом, чтобы не прострелить себе ногу.