运行以下代码时,当人们期望它为 FooViewController
时,defaultModuleName
内部的self
为ReactViewController
。为什么?
class ReactViewController: UIViewController {
var moduleName: String = defaultModuleName
static var defaultModuleName: String {
let t = String(reflecting: self) // Also tried NSStringFromClass
guard let s = t.split(separator: ".").last else { return "" }
guard let r = s.range(of: "ViewController") else { return "" }
return String(s.prefix(upTo: r.lowerBound))
}
}
class FooViewController: ReactViewController {
override func viewDidLoad() {
super.viewDidLoad();
print(moduleName); // Prints "React"
}
}
这很有趣;看来属性初始评估器中可用的self
仅仅是属性所定义的类型,而不是构造实例的动态类型。
一个最小的例子是:
class C {
static var foo: String { return "(self)" }
let bar = foo // the implicit 'self' in the call to 'foo' is always C.
}
class D : C {}
print(D().bar) // C
在 bar
的属性启动器中,隐式 self
是 C.self
,而不是 D.self
;尽管我们正在构建D
实例。这就是对foo
的呼吁将其视为self
。
这还可以防止class
成员从属性初始评估器中撤回:
class C {
class var foo: String { return "C" }
let bar = foo
}
class D : C {
override class var foo: String { return "D" }
}
print(D().bar) // C
因此,我将其视为错误,并在此处提交了报告。
在修复之前,一个简单的解决方案是使用懒惰属性,因为现在self
是实际实例(在第一次访问属性时),我们得到的可以使动态获得动态使用type(of: self)
的类型。
例如:
class C {
static var foo: String { return "(self)" }
// private(set) as the property was a 'let' in the previous example.
lazy private(set) var bar = type(of: self).foo
}
class D : C {}
print(D().bar) // D
应用于您的示例:
class ReactViewController : UIViewController {
lazy var moduleName = type(of: self).defaultModuleName
static var defaultModuleName: String {
let t = String(reflecting: self) // Also tried NSStringFromClass
guard let s = t.split(separator: ".").last else { return "" }
guard let r = s.range(of: "ViewController") else { return "" }
return String(s.prefix(upTo: r.lowerBound))
}
}
class FooViewController : ReactViewController {
override func viewDidLoad() {
super.viewDidLoad()
print(moduleName) // Prints "Foo"
}
}
您只需要通过self
而不是type(of: self)
,然后使用String(describing:)
Initializer。
class ClassA {
static var className: String {
return String(describing: self)
}
}
class ClassB: ClassA { }
print(ClassB.className) // prints "ClassB"
编辑:澄清var moduleName: String = defaultModuleName
更新。假设我将此行添加到上面的示例(同一想法):
class ClassA {
// This is a property of ClassA -> it gets implicitly initialized
// when ClassA does -> it uses ClassA.className for its value
var instanceClassName = className
static var className: String {
return String(describing: self)
}
}
class ClassB: ClassA { }
print(ClassB().instanceClassName) // prints "ClassA"
这个新的instanceClassName
不是静态的,因此它是ClassA
上的实例属性。因此,当初始化ClassA
初始化时(不是)初始化它。Ergo是在ClassA
中设置的属性,使用className
的引用将打印出ClassA
。