/ / Dlaczego typ podklasy nie jest dostępny, gdy właściwość instancji jest inicjowana przez statyczny element członkowski? - szybki

Dlaczego typ podklasy jest niedostępny, gdy właściwość instancji jest inicjowana przez statyczny element członkowski? - szybki

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 № 1

To 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.