在Swift中获取类的名称非常简单:
import Foundation
class Gen<T> {
init() { }
}
func printName(obj: AnyObject) {
dump(NSStringFromClass(obj.dynamicType))
}
let a: Gen<String> = Gen()
let b: Gen<Int> = Gen()
printName(a)
printName(b)
但是,如果运行上述代码两次,则不会产生相同的结果。
那么有没有办法以稳定的方式实现printName
(获取专门的通用类的名称)?那就是代码的多次运行为同一个类打印相同的字符串?
我正在使用最新的Xcode测试版。
一些额外要求:
printName
的班级数量很高。答案 0 :(得分:7)
很抱歉让人失望,但这似乎不太可能,除非您积极编写新的编译器。
检查此source。作者很好地描述了它:Note that the isa pointer of the two objects is not identical, even though both are an instance of WrapperClass. Evidently, a specialization of a generic class is a separate class at runtime.
我还在玩@objc
,但这只是改变了类的命名空间,但显然对类名没有影响。
答案 1 :(得分:6)
从Xcode 6.3 beta 1开始的新功能
好消息!从6.3开始你可以做到:toString(Gen<Int>())
然后打印:Gen<Swift.Int>
OLD答案:
如果范围可以缩减为T:AnyObject,那么您可以按如下方式实现它:
class Gen<T: AnyObject> {
var className: String {
return "Gen<\(NSStringFromClass(T.self))>"
}
}
答案 2 :(得分:2)
这将打印Gen
,并且就我所知的任何课程都适用:
class Gen<T> {
init() { }
}
func printName(obj: AnyObject) {
let desc = reflect(obj).summary
let scanner = NSScanner(string: desc)
var name: NSString?
scanner.scanUpToString(".", intoString: nil)
scanner.scanString(".", intoString: nil)
scanner.scanUpToString("", intoString: &name)
if let n = name {
println(n)
}
}
但是,它不会为您提供专门的类型 - 例如Gen<String>
或Gen<Int>
- 我现在只在在线REPL上测试它,因此结果可能会在项目或游乐场。
您是否需要它来提供专门的类型,或者类名是否足够?
编辑:我将@ relejo的答案和我的答案结合起来,使用协议和他建议的计算属性给你专门的类型:
protocol GenericClassName {
var className: String { get }
}
class Gen<T: AnyObject>: GenericClassName {
init() { }
var className: String {
return NSStringFromClass(T.self)
}
}
func printName<U: GenericClassName>(obj: U) {
let desc = reflect(obj).summary
let scanner = NSScanner(string: desc)
var name: NSString?
scanner.scanUpToString(".", intoString: nil)
scanner.scanString(".", intoString: nil)
scanner.scanUpToString("", intoString: &name)
if let n = name {
println(n + "<\(obj.className)>")
}
}
let gen1 = Gen<NSString>()
let gen2 = Gen<NSNumber>()
printName(gen1) // Outputs: Gen<NSString>
printName(gen2) // Outputs: Gen<NSNumber>
再次编辑:第二个想法,如果你要为每个泛型类添加一个计算属性(你说你只有3-4个,对吧?)那么就让它看起来像这样:
class Gen<T: AnyObject> {
init() { }
var className: String {
return (reflect(self).summary.componentsSeparatedByString(".").last ?? "") + "<\(NSStringFromClass(T.self))>"
}
}
现在您可以将类名称作为字符串获取:
let gen1 = Gen<NSString>()
println(gen1.className) // Output: Gen<NSString>
这样你就不会搞乱协议或全局函数或NSScanner
(这可能很慢......)。
答案 3 :(得分:0)
建立最佳答案,如果你只想要实际的类名,不包括&#34;模块&#34;它只是被#34;分开。&#34;并获取该数组的最后一个元素。我需要获取通用名称以编写通用核心数据函数,以便能够调用 NSManagedObject.insertObjectIntoContext ,这样我就不会调用 NSEntityDescription.insertNewObject。 .. 到处都是。
代码:
func insertNewObjectIntoContext<E>(moc: NSManagedObjectContext) -> E {
return NSEntityDescription.insertNewObjectForEntityForName(toString(E).componentsSeparatedByString(".").last!, inManagedObjectContext: moc) as! E
}
这个问题的关注部分是:
toString(E).componentsSeparatedByString(".").last! // returns E - rather than ModuleName.E
答案 4 :(得分:-1)
也许这可能暗示您正在寻找的解决方案:
class Gen<T> {
init() { }
}
enum ClassID: String {
case GenInt = "Gen<Int>"
case GenString = "Gen<String>"
func classFromID(stringID: String) -> AnyClass? {
if let classID = ClassID(rawValue: stringID) {
switch classID {
case .GenInt:
return Gen<Int>().dynamicType
case .GenString:
return Gen<String>().dynamicType
}
}
return nil
}
}
简而言之,构建自己的类ID,以及相关函数来恢复它们。您保留安全性,因为如果某个类不是已知的类,则不会使用它。