Как можно динамически преобразовать имя класса Swift?

Я знаю утилиту командной строки swift-demangle. Я ищу что-то, что позволит мне сделать это из самого Swift.

Я был взволнован, когда увидел это после запуска :target modules dump symtab из Swift REPL, но я не знаю, как вызвать swift_demangleSimpleClass.

module dump

Кажется, существует команда @asmname, которая позволяет вызывать частные функции Swift, но я не смог заставить это работать.

Вероятно, я просто напишу парсер на основе regex для этого, но вызов чего-то в самой структуре Swift кажется более безопасным.

Ответы

Ответ 1

func typename (thing:Any) -> String{
    let name = _stdlib_getTypeName(thing)
    let demangleName = _stdlib_demangleName(name)
    return demangleName.componentsSeparatedByString(".").last!
}

Ответ 2

В настоящий момент (XCode 6 плюс 7 бета) для классов на самом высоком уровне вы просто получаете свое приложение плюс точку плюс свое имя класса. Так что это будет что-то вроде MyApp.MyClass. Но как только вы используете подкласс, описание создается в следующих трех разделах:

  • Описание начинается с _Tt, который обозначает цель (имя пакета/приложения)
  • После этого у вас будет одна или несколько букв, таких как C P F, которая обозначает класс, протокол или функцию. Они находятся в обратном порядке в соответствии с вложением
  • Затем вы получите серию чисел плюс имя, где число - это длина имени. каждый из них предназначен для цели, класса, протокола или функции, как указано в начале описания.

Здесь особый случай. В описании также будет информация о сигнатуре функции.

Например, у вас может быть _TtCFCC5MyApp7MyClass10MySubClass6myFuncFS0_FT_T_L_11MySubSubClass

Это будет описание MySubSubClass в следующем коде:

class MyClass {
   class MySubClass {
     func myFunc() {
       class MySubSubClass {
       }
     }
   }
}

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

Обновление: теперь Demangle преобразуется в быстрый. Вы можете найти его здесь: https://github.com/mattgallagher/CwlDemangle/blob/master/CwlDemangle/CwlDemangle.swift

Ответ 3

Обновление: по состоянию на XCode6 beta 5, NSStringFromClass, похоже, автоматически возвращает имена с именами классов, поэтому этот код больше не нужен.

Должен быть встроенный способ сделать это, но до тех пор, пока это не произойдет, это расширение должно сделать трюк:

import Foundation

public func demangleClassName(mangled: String) -> String {
    let scanner = NSScanner(string: mangled)
    if (!scanner.scanString("_TtC", intoString: nil)) {
        // not a mangled swift class name: Core Foundation, etc. have no module prefix
        return mangled
    }

    var demangled = ""
    var len : Int = 0
    while (!scanner.atEnd && scanner.scanInteger(&len)) {
        let range = Range(start:advance(mangled.startIndex, scanner.scanLocation), end: advance(mangled.startIndex, scanner.scanLocation + len))
        let part = mangled.substringWithRange(range)
        if (countElements(demangled) > 0) {
            demangled += "."
        }
        demangled += part
        scanner.scanLocation += len // skip to the next segment that may be prefixed by the number
    }

    return demangled
}

public extension NSObject {
    /// demangle the current Swift object class name, as per mangling description at: http://www.eswick.com/2014/06/inside-swift/
    public class func demangledClassName() -> String {
        return demangleClassName(className())
    }
}