Swift 3 - поиск имени/типа класса

В моем приложении используются сотни подклассов класса рисования. (Эти подклассы автоматически генерируются из художественных файлов.)

В идеале я хотел бы загрузить имена некоторых подклассов, проверить, действительно ли эти подклассы доступны, и если да, то создать их экземпляры.

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

Таким образом, одним из возможных решений является использование таблицы поиска.

Вопрос: если я жестко закодирую Dictionary со всеми именами моих подклассов в качестве ключей, какой синтаксис мне нужен для values , чтобы извлеченное значение могло быть создано как экземпляр объект требуемого класса? Спасибо за любую помощь.


person Womble    schedule 10.10.2016    source источник
comment
Вы можете вернуть foo.self, чтобы получить объект класса (типа SomeDrawingProtocol.Type).   -  person kennytm    schedule 10.10.2016


Ответы (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, если это необходимо.

person Greg    schedule 10.10.2016
comment
Спасибо, Грег. Очень ценю! Как только я добавил вызов init() в прототип и сказал автоматизатору добавить необходимые вызовы init() в каждый подкласс, все заработало, как было заявлено. Ваше здоровье! - person Womble; 10.10.2016
comment
@Womble (не уверен, что вы это сделали), если все имеет тип SomeDrawingProtocol, вы можете просто использовать SomeDrawingProtocol.Type в качестве типа для элементов словаря, если он объявляет некоторый инициализатор, который реализуют все ваши классы. - person Greg; 10.10.2016
comment
Спасибо, Грег. Я действительно использовал SomeDrawingProtocol.Type в качестве типа для словаря. Это был тот init(), который бросал меня... Xcode услужливо (?) пытался заставить меня создать экземпляр типа (из:)... init. :) Большое ура. - person Womble; 10.10.2016