Dans Swift, une instance func
ne peut pas appeler un static/class func
Sans préfixer l'appel de méthode avec le nom de la classe. OR vous pouvez utiliser type(of: self)
, par exemple
class Foo {
static func doIt() { }
func callIt() {
Foo.doIt() // This works
type(of: self).doIt() // Or this
doIt() // This doesn't compile (unresolved identifier)
}
}
Ma question est, quelle est la différence ici? Est-ce juste une question de style de codage, ou y a-t-il une différence, par exemple envoi statique ou dynamique en cours?
S'il s'agit simplement d'un style de codage, quel est le style préféré?
Il existe deux différences principales.
self
à l'intérieur de la méthode statiqueLe métatype sur lequel vous appelez la méthode statique est disponible dans la méthode sous la forme self
(il est simplement passé comme paramètre implicite). Par conséquent, si vous appelez doIt()
on type(of: self)
, self
sera la dynamique métatype de l'instance. Si vous l'appelez sur Foo
, self
sera Foo.self
.
class Foo {
static func doIt() {
print("hey I'm of type \(self)")
}
func callDoItOnDynamicType() {
type(of: self).doIt() // call on the dynamic metatype of the instance.
}
func classDoItOnFoo() {
Foo.doIt() // call on the metatype Foo.self.
}
}
class Bar : Foo {}
let f: Foo = Bar()
f.callDoItOnDynamicType() // hey I'm of type Bar
f.classDoItOnFoo() // hey I'm of type Foo
Cette différence peut être vraiment importante pour les méthodes d'usine, car elle détermine le type d'instance que vous créez.
class Foo {
required init() {}
static func create() -> Self {
return self.init()
}
func createDynamic() -> Foo {
return type(of: self).create()
}
func createFoo() -> Foo {
return Foo.create()
}
}
class Bar : Foo {}
let f: Foo = Bar()
print(f.createDynamic()) // Bar
print(f.createFoo()) // Foo
(Martin a déjà couvert cela, mais je pensais que je l'ajouterais pour le plaisir de terminer. )
Pour les méthodes class
qui sont remplacées dans les sous-classes, la valeur du métatype sur lequel vous appelez la méthode détermine quelle implémentation appeler.
S'il est appelé sur un métatype connu au moment de la compilation (par exemple Foo.doIt()
), Swift est capable de répartir statiquement l'appel. Cependant, si vous appelez la méthode sur un métatype qui n'est pas connu jusqu'à l'exécution (par exemple type(of: self)
), l'appel de méthode sera dynamiquement envoyé à l'implémentation correcte pour le métatype valeur.
class Foo {
class func doIt() {
print("Foo's doIt")
}
func callDoItOnDynamicType() {
type(of: self).doIt() // the call to doIt() will be dynamically dispatched.
}
func classDoItOnFoo() {
Foo.doIt() // will be statically dispatched.
}
}
class Bar : Foo {
override class func doIt() {
print("Bar's doIt")
}
}
let f: Foo = Bar()
f.callDoItOnDynamicType() // Bar's doIt
f.classDoItOnFoo() // Foo's doIt
Pour une méthode class
, cela fait une différence si la méthode est remplacée dans une sous-classe:
class Foo {
class func doIt() {
print("Foo doit")
}
func callViaClassname() {
Foo.doIt()
}
func callViaTypeOf() {
type(of: self).doIt()
}
}
class Bar: Foo {
override class func doIt() {
print("Bar doit")
}
}
Bar().callViaClassname() // Foo doit
Bar().callViaTypeOf() // Bar doit
Ceci est également documenté dans "Types" dans le Swift Language Reference:
Vous pouvez utiliser une expression
type(of:)
avec une instance d'un type pour accéder au type d'exécution dynamique de cette instance en tant que valeur, ...
Je ne connais pas de différence pour une méthode Correction: Voir réponse de Hamish pour la différence entre les méthodes statiques et de classe.static
(qui est final
et ne peut pas être remplacée dans une sous-classe).