Swift универсальный тип ссылки
У меня есть проблемы, пытающиеся ограничить общие требования к типу только ссылочными типами. Вот пример кода:
class WeakHolder<Element: AnyObject> {
weak var element: Element?
init(element: Element) {
self.element = element
}
}
protocol Animal: class { }
class Dog: Animal { }
let dog: Animal = Dog()
let holder = WeakHolder<Animal>(element: dog) // Error: Using "Animal" as a concrete type conforming to protocol 'AnyObject' is not supported.
Если я изменю общее требование на <Element: class>
Я получаю ошибку class constraint can only appear on protocol declarations
,
Это ограничение дженериков? Маркировки протокола как класса достаточно, чтобы иметь слабую ссылку на этот протокол, нет ли эквивалента в дженериках?
2 ответа
Ответ прост: у вас не может быть универсального типа, который является протоколом.
Написание синтаксиса проясняет, как это работает: class/struct GenericType<TypeName: TypeConstraints> {}
let thing = GenericType<Type>() where Type is a class or struct that adheres to any constraints
Протокол, требующий принятия типов в качестве класса, означает, что любые приемники являются классами, но сам протокол все еще не является типом.
Возможно, дженерики могут в какой-то момент поддерживать протоколы, но для этого потребуется изменить общий подход к протоколам или дженерикам. Хотя ваш конкретный пример может быть возможен с меньшим объемом закулисной работы, возможно, это может быть реализовано в какой-то момент.
Вы можете взглянуть на Манифест Обобщения, если хотите увидеть направление, в котором они движутся. Просматривая его, я не нашел ничего, что напрямую связано с вашим вариантом использования, но он довольно специфичен, поэтому он не может быть включен в параметры документа.
Другое решение, которое сработало в моем конкретном случае, заключается в следующем:
class WeakHolder<Element: AnyObject> {
weak var element: Element?
init(element: Element) {
self.element = element
}
}
protocol Animal: class { }
class Dog: Animal { }
let dog: Animal = Dog()
let holder = WeakHolder<AnyObject>(element: dog as AnyObject)
При доступе к элементу мне просто нужно выполнить возврат к своему протоколу. Конечно, я потеряю безопасность времени компиляции при использовании этого класса с типами значений, но это не проблема в моей ситуации.