如何动态解码Swift类名?

jps*_*sim 16 iphone ios swift ios8

我知道swift-demangle命令行实用程序.我正在寻找能让我从Swift本身做到这一点的东西.

:target modules dump symtab从Swift REPL 跑出来之后,当我看到这个时,我很兴奋,但我不知道怎么打电话swift_demangleSimpleClass.

模块转储

似乎有一个@asmname命令允许调用私有Swift函数,但我无法让它工作.

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

iUr*_*rii 9

Swift 5

You can use Swift's swift_demangle function to demangle names but it's not exported by default so you need to import first:

import Darwin

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
}

Run Code Online (Sandbox Code Playgroud)


mar*_*rux -1

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