Коли запускається наступний код, self
всередині defaultModuleName
є ReactViewController
коли можна було б очікувати, що це буде FooViewController
. Чому?
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"
}
}
Відповіді:
2 для відповіді № 1Це досить цікаво; виявляється, що 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
; незважаючи на те, що ми "будуємо a 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"
}
}
0 для відповіді № 2
Вам просто потрібно пройти self
замість type(of: self)
і використовуйте String(describing:)
ініціалізатор
class ClassA {
static var className: String {
return String(describing: self)
}
}
class ClassB: ClassA { }
print(ClassB.className) // prints "ClassB"
EDIT: роз'яснення на 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
ініціалізовано (ні коли ініціалізується ClassB). Ergo, власність, що встановлюється в межах ClassA
, використовуючи посилання на className
, буде роздруковано ClassA
.