Pull to refresh

Associated Objects

В описании расширений сказано, что нельзя добавлять свойства хранения, но что если это не так и такое делать на самом деле можно?

Этот функционал перекочевал в 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 может быть любой тип, а использовать эти методы можно не только в таком случае, всему своя фантазия, но нужно быть очень осторожным с таким инструментом, чтобы не прострелить себе ногу.

Источники

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.