如何动态解码Swift类名?

时间:2014-06-20 07:07:53

标签: ios iphone swift ios8

我知道swift-demangle命令行实用程序。我正在寻找能让我从Swift本身做到的事情。

当我在Swift REPL中运行:target modules dump symtab之后看到此内容时,我很兴奋,但我不知道如何拨打swift_demangleSimpleClass

module dump

似乎有一个@asmname命令允许调用私有的Swift函数,但我还没有能够让它工作。

我可能最终会为此编写一个基于正则表达式的解析器,但在Swift框架中调用某些内容似乎更安全。

4 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:3)

目前(XCode 6加7测试版)对于最高级别的课程,您只需获取您的appname加上一个点加上您的类名。所以它将类似MyApp.MyClass但是当你使用子类时,描述将在以下3个部分中构建:

  1. 描述以_Tt开头,它代表目标(包/应用名称)
  2. 之后你会有一个或多个字母,如C P F代表Class,Protocol或Function。根据嵌套
  3. ,它们的顺序相反
  4. 然后,您将获得一系列数字以及一个名称,其中数字是名称的长度。这些中的每一个都用于描述开头所指定的目标,类,协议或功能。
  5. 这里的功能是一个特例。该描述还将提供有关函数签名的一些信息。

    例如,您可以拥有_TtCFCC5MyApp7MyClass10MySubClass6myFuncFS0_FT_T_L_11MySubSubClass

    这将是以下代码中MySubSubClass的描述:

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

    在这里,您可以找到一些sample code,它将该描述解析为易于使用的属性和数组。

    更新:Demangle现在转换为swift。您可以在此处找到它:https://github.com/mattgallagher/CwlDemangle/blob/master/CwlDemangle/CwlDemangle.swift

答案 2 :(得分:2)

Swift 5

您可以使用 Swift 的 swift_demangle 函数来对名称进行解码,但默认情况下它不会导出,因此您需要先导入:

typealias Swift_Demangle = @convention(c) (_ mangledName: UnsafePointer<UInt8>?,
                                           _ mangledNameLength: Int,
                                           _ outputBuffer: UnsafeMutablePointer<UInt8>?,
                                           _ outputBufferSize: UnsafeMutablePointer<Int>?,
                                           _ flags: UInt32) -> UnsafeMutablePointer<Int8>?

func swift_demangle(_ mangled: String) -> String? {
    let RTLD_DEFAULT = dlopen(nil, RTLD_NOW)
    if let sym = dlsym(RTLD_DEFAULT, "swift_demangle") {
        let f = unsafeBitCast(sym, to: Swift_Demangle.self)
        if let cString = f(mangled, mangled.count, nil, nil, 0) {
            defer { cString.deallocate() }
            return String(cString: cString)
        }
    }
    return nil
}

// How to use
if let s = swift_demangle("$s20MyPlayground_Sources4TestC4testSSyF") {
    print(s) // MyPlayground_Sources.Test.test() -> Swift.String
}

答案 3 :(得分:-1)

更新:从XCode6 beta 5开始,NSStringFromClass似乎会自动返回demangled类名,因此不再需要此代码。

应该有一种内置的方法来做到这一点,但在此之前,这个扩展应该可以解决这个问题:

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's class name, as per mangling description at: http://www.eswick.com/2014/06/inside-swift/
    public class func demangledClassName() -> String {
        return demangleClassName(className())
    }
}