#swift #dynamic #metaprogramming #metaclass
#swift #динамический #метапрограммирование #метакласс
Вопрос:
Мое приложение использует сотни подклассов класса рисования. (Эти подклассы автоматически создаются из файлов art.)
В идеале я хотел бы загрузить имена некоторых подклассов, проверить, действительно ли эти подклассы доступны, и если да, создать их экземпляр.
Однако, казалось бы, отсутствие NSClassFromString
функциональности в стиле pure Swift означает, что я вынужден заранее объявлять все имена своих классов компилятору.
например, получение класса с помощью очень длинного, утомительного оператора switch:
func drawingObjectFromClassName(_ className: String) -> SomeDrawingProtocol?
{
switch className {
case "foo": return foo()
case "bar": return bar()
// etc.
default:
print("Warning: no class found for className: ", className)
return nil
}
}
Но я также хотел бы проверить наличие подкласса (например, «foo») без создания экземпляра объекта.
Я мог бы дублировать этот оператор switch для этого, но тогда у меня было бы два идентичных набора ключей для обслуживания. Фу.
Итак, одним из возможных решений является использование таблицы поиска.
Вопрос: если я жестко запрограммирую словарь со всеми именами моих подклассов в качестве ключей, какой синтаксис мне нужен для значений, чтобы полученное значение могло быть создано как объект требуемого класса? Спасибо за любую помощь.
Комментарии:
1. Вы можете вернуться
foo.self
, чтобы получить объект класса (типаSomeDrawingProtocol.Type
).
Ответ №1:
protocol P {
init()
}
extension String : P { }
extension Int : P { }
let types : [String : P.Type] = [
"str": String.self,
"int": Int.self
]
if let type = types["int"] {
let object = type.init()
assert(object is Int)
}
Теперь вы можете использовать все стандартные операции со словарем, чтобы проверить, присутствуют ли вещи, использовать типы, которые вы извлекаете, как хотите, И т. Д.
Я определил протокол в приведенном выше примере, чтобы иметь общий инициализатор (который, вероятно, вам понадобится в большинстве случаев), но вы можете просто использовать Any.Type
и использовать тип, используя as Int.Type
, если это необходимо.
Комментарии:
1. Спасибо, Грег. Очень признателен! Как только я добавил вызов init() в прототип и попросил automator добавить необходимые вызовы init() для каждого подкласса, он работал так, как было объявлено. Приветствия!
2. @Womble (не уверен, что вы это сделали) если все имеет тип
SomeDrawingProtocol
, вы можете просто использоватьSomeDrawingProtocol.Type
в качестве типа для элементов словаря, если он объявляет некоторый инициализатор, который реализуют все ваши классы.3. Спасибо, Грег. Я действительно использовал SomeDrawingProtocol. Введите в качестве типа для словаря. Это был тот init(), который бросил меня… Xcode был полезен (?), Пытаясь заставить меня создать экземпляр type(of:) … init . 🙂 Большое приветствие.