Быстрый поиск имени / типа 3-го класса

#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 . 🙂 Большое приветствие.