/ / Чому тип підкласу недоступний, коли властивість екземпляра ініціалізується статичним членом? - стрімкий

Чому тип підкласу не доступний, якщо властивість екземпляра ініціалізується статичним членом? - швидкий

Коли запускається наступний код, 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.