web-dev-qa-db-fra.com

Swift appel de méthodes statiques: type (of: self) vs nom de classe explicite

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é?

24
Orion Edwards

Il existe deux différences principales.

1. La valeur de self à l'intérieur de la méthode statique

Le 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

2. L'envoi de la méthode statique

(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
29
Hamish

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 static (qui est final et ne peut pas être remplacée dans une sous-classe).  Correction: Voir réponse de Hamish pour la différence entre les méthodes statiques et de classe.

4
Martin R