/ / ¿Por qué el tipo de subclase no está disponible cuando una propiedad de instancia es inicializada por un miembro estático? - rápido

¿Por qué el tipo de subclase no está disponible cuando un miembro estático inicializa una propiedad de instancia? - rápido

Cuando se ejecuta el siguiente código, el self dentro de defaultModuleName es ReactViewController cuando uno esperaría que fuera FooViewController. ¿Por qué?

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"
}

}

Respuestas

2 para la respuesta № 1

Esto es bastante interesante; parece que el self disponible en un inicializador de propiedad es simplemente el tipo en el que se define la propiedad, en lugar del tipo dinámico de la instancia que se está construyendo.

Un ejemplo más mínimo sería:

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

En el inicializador de propiedades para bar, lo implícito self es C.selfno D.self; A pesar del hecho de que estamos "construyendo una D ejemplo. Así que eso es lo que la llamada foo ve como self.

Esto también evita class el miembro anula la llamada desde los inicializadores de propiedades:

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

Por lo tanto considero esto como un error, y tengo presentó un informe aquí.

Hasta que se arregle, una solución simple es usar un perezoso propiedad en cambio, como ahora self es la instancia real (al acceder a la propiedad por primera vez), la cual obtenemos puede obtener el tipo dinámico con type(of: self).

Por ejemplo:

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

Aplicado a tu ejemplo:

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 para la respuesta № 2

Solo necesitas pasar self en lugar de type(of: self), y usar el String(describing:) inicializador

class ClassA {

static var className: String {
return String(describing: self)
}

}

class ClassB: ClassA { }

print(ClassB.className) // prints "ClassB"

EDIT: aclaración sobre el var moduleName: String = defaultModuleName actualizar. Supongamos que agrego esta línea al ejemplo anterior (la misma idea):

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"

Esta nueva instanceClassName no es estático, por lo que es una propiedad de instancia en ClassA. Por lo tanto, se inicializa cuando ClassA se inicializa (no cuando se inicializa ClassB). Ergo, una propiedad que se establece dentro de ClassA, usando una referencia a classNamese imprimirá ClassA.