Po uruchomieniu poniższego kodu self
wnętrze defaultModuleName
jest ReactViewController
kiedy można by się tego spodziewać FooViewController
. Czemu?
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"
}
}
Odpowiedzi:
2 dla odpowiedzi № 1To jest całkiem interesujące; wydaje się, że self
dostępne w inicjatorze właściwości to tylko typ, w którym właściwość jest zdefiniowana, a nie typ dynamiczny konstruowanej instancji.
Bardziej minimalnym przykładem byłoby:
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
W inicjatorze właściwości dla bar
, dorozumiany self
jest C.self
, nie D.self
; pomimo faktu, że „rekonstruujemy plik D
instancja. Więc to jest to, do czego dzwonisz foo
widzi jako self
.
To również zapobiega class
element członkowski zastępuje wywołanie z inicjatorów właściwości:
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
Dlatego uważam to za błąd i mam złożył raport tutaj.
Do czasu rozwiązania prostego rozwiązania jest użycie pliku leniwy zamiast tego, jak teraz self
jest rzeczywistą instancją (przy pierwszym dostępie do właściwości), za pomocą której możemy uzyskać typ dynamiczny type(of: self)
.
Na przykład:
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
Zastosowany do twojego przykładu:
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 dla odpowiedzi nr 2
Musisz tylko zdać self
zamiast type(of: self)
i użyj String(describing:)
inicjator.
class ClassA {
static var className: String {
return String(describing: self)
}
}
class ClassB: ClassA { }
print(ClassB.className) // prints "ClassB"
EDYCJA: wyjaśnienie dotyczące var moduleName: String = defaultModuleName
aktualizacja. Załóżmy, że dodam tę linię do powyższego przykładu (ten sam pomysł):
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"
To nowe instanceClassName
nie jest statyczny, więc jest właściwością instancji on ClassA
. Dlatego jest inicjowany, gdy ClassA
jest zainicjowany (nie podczas inicjalizacji ClassB). Ergo, nieruchomość osadzona w środku ClassA
, używając odniesienia do className
, wydrukuje ClassA
.