Quelle est la différence entre func statique et func de classe dans Swift?

334

Je peux voir ces définitions dans la bibliothèque Swift:

extension Bool : BooleanLiteralConvertible {
    static func convertFromBooleanLiteral(value: Bool) -> Bool
}

protocol BooleanLiteralConvertible {
    typealias BooleanLiteralType
    class func convertFromBooleanLiteral(value: BooleanLiteralType) -> Self
}

Quelle est la différence entre une fonction membre définie comme static funcet une autre définie comme class func? Est-ce simplement staticpour les fonctions statiques des structures et des énumérations, ainsi que classpour les classes et les protocoles? Y a-t-il d'autres différences que l'on devrait connaître? Quelle est la justification de cette distinction dans la syntaxe elle-même?

Jean-Philippe Pellet
la source
3
Il n'y a pas vraiment de différence. Ils ne pouvaient pas utiliser la classe func dans une structure, je suppose, d'où la fonction statique. struct func aurait été un bon candidat. C'est un peu énervé si vous me demandez mais bon, ce sont les mots.
fabrice truillot de chambrier
2
Question bonus, alors: une structure peut-elle se conformer à un protocole qui définit un class func? Avec les informations dont nous disposons actuellement, cette distinction semble plutôt inutile, non?
Jean-Philippe Pellet
3
Oui, vous pouvez. Étrange n'est-ce pas?
fabrice truillot de chambrier
7
la différence est que vous pouvez remplacer class funcs
Fattie
1
À considérer:error: class methods are only allowed within classes; use 'static' to declare a static method
Gabriel Goncalves

Réponses:

238

Est-ce simplement que statique est pour les fonctions statiques des structures et des énumérations, et la classe pour les classes et les protocoles?

Voilà la principale différence. Certaines autres différences sont que les fonctions de classe sont distribuées dynamiquement et peuvent être remplacées par des sous-classes.

Les protocoles utilisent le mot-clé class, mais il n'exclut pas les structures de l'implémentation du protocole, ils utilisent simplement statique à la place. La classe a été choisie pour les protocoles, il n'y aurait donc pas besoin d'un troisième mot-clé pour représenter statique ou classe.

De Chris Lattner sur ce sujet:

Nous avons envisagé d'unifier la syntaxe (par exemple en utilisant "type" comme mot-clé), mais cela ne fait pas simplement des choses. Les mots clés "classe" et "statique" sont bons pour la familiarité et sont assez descriptifs (une fois que vous comprenez comment + les méthodes fonctionnent), et ouvrent la porte à l'ajout potentiel de méthodes vraiment statiques aux classes. La bizarrerie principale de ce modèle est que les protocoles doivent choisir un mot-clé (et nous avons choisi "classe"), mais dans l'ensemble c'est le bon compromis.

Et voici un extrait qui montre certains des comportements de substitution des fonctions de classe:

class MyClass {
    class func myFunc() {
        println("myClass")
    }
}

class MyOtherClass: MyClass {
    override class func myFunc() {
        println("myOtherClass")
    }
}

var x: MyClass = MyOtherClass()
x.dynamicType.myFunc() //myOtherClass
x = MyClass()
x.dynamicType.myFunc() //myClass
Connor
la source
4
Aha, point très important que les fonctions de classe sont distribuées dynamiquement! Mais pourriez-vous fournir un tel exemple? Il faudrait écrire le nom de la classe quelque part, non? Alors pourquoi ne pas choisir statiquement l'implémentation de cette classe?
Jean-Philippe Pellet
1
Autre question complémentaire: d'où avez-vous obtenu le devis?
Jean-Philippe Pellet
ma compréhension est que les fonctions de classe fonctionnent à peu près de la même manière que les méthodes objc + sous le capot
Connor
1
Puis-je fournir un lien de réponse plus simple ici? stackoverflow.com/questions/29636633/…
allenlinli
1
@ Jean-PhilippePellet Dans l'exemple ci-dessus ... si vous utilisez à la static func myFunc()place de class func myFuncvous obtiendrez l'erreur suivante l: ne peut pas remplacer la méthode statique . Pourquoi? Parce que c'est comme si c'était marqué final. Pour plus d'informations. Voir la réponse de nextD ci-dessous. A également x.dynamicTypeété remplacé partype(of:x)
Honey
246

Pour être plus clair, je fais un exemple ici,

class ClassA {
  class func func1() -> String {
    return "func1"
  }

  static func func2() -> String {
    return "func2"
  }

  /* same as above
  final class func func2() -> String {
    return "func2"
  }
  */
}

static func est identique à final class func

Parce qu'il l'est final, nous ne pouvons pas le remplacer dans la sous-classe comme ci-dessous:

class ClassB : ClassA {
  override class func func1() -> String {
    return "func1 in ClassB"
  }

  // ERROR: Class method overrides a 'final` class method
  override static func func2() -> String {
    return "func2 in ClassB"
  }
}
Jake Lin
la source
18
vous champion, grande réponse .. je cherchais cette différence .. Jake !!
Abhimanyu Rathore
5
Parfait. Impressionnant.
Mehul
5
Cela devrait être marqué comme la bonne réponse. Propre et net!
abhinavroy23
1
Meilleure explication! Cela m'a conduit à un autre doute. Y a-t-il une raison explicite d'utiliser un «func de classe»? Je veux dire que si vous utilisez simplement «func», il peut également être remplacé de la même manière, alors quelle est la différence?
Marcos Reboucas
1
@MarcosReboucas, si je comprends bien votre question, class funcest différent de la normale, funcbien que les deux puissent être remplacées. Mais funcc'est pour une instance / un objet et class funcpeut être accessible via la classe commeClassA.classFunc()
Jake Lin
78

J'ai fait quelques expériences dans une aire de jeux et j'ai tiré quelques conclusions.

TL; DR entrez la description de l'image ici

Comme vous pouvez le voir, dans le cas de class, l'utilisation class funcou static funcn'est qu'une question d'habitude.

Exemple de terrain de jeu avec explication:

class Dog {
    final func identity() -> String {
        return "Once a woofer, forever a woofer!"
    }

    class func talk() -> String {
        return "Woof woof!"
    }

    static func eat() -> String {
        return "Miam miam"
    }

    func sleep() -> String {
        return "Zzz"
    }
}

class Bulldog: Dog {
    // Can not override a final function
//    override final func identity() -> String {
//        return "I'm once a dog but now I'm a cat"
//    }

    // Can not override a "class func", but redeclare is ok
    func talk() -> String {
        return "I'm a bulldog, and I don't woof."
    }

    // Same as "class func"
    func eat() -> String {
        return "I'm a bulldog, and I don't eat."
    }

    // Normal function can be overridden
    override func sleep() -> String {
        return "I'm a bulldog, and I don't sleep."
    }
}

let dog = Dog()
let bullDog = Bulldog()

// FINAL FUNC
//print(Dog.identity()) // compile error
print(dog.identity()) // print "Once a woofer, forever a woofer!"
//print(Bulldog.identity()) // compile error
print(bullDog.identity()) // print "Once a woofer, forever a woofer!"

// => "final func" is just a "normal" one but prevented to be overridden nor redeclared by subclasses.


// CLASS FUNC
print(Dog.talk()) // print "Woof woof!", called directly from class
//print(dog.talk()) // compile error cause "class func" is meant to be called directly from class, not an instance.
print(Bulldog.talk()) // print "Woof woof!" cause it's called from Bulldog class, not bullDog instance.
print(bullDog.talk()) // print "I'm a bulldog, and I don't woof." cause talk() is redeclared and it's called from bullDig instance

// => "class func" is like a "static" one, must be called directly from class or subclassed, can be redeclared but NOT meant to be overridden.

// STATIC FUNC
print(Dog.eat()) // print "Miam miam"
//print(dog.eat()) // compile error cause "static func" is type method
print(Bulldog.eat()) // print "Miam miam"
print(bullDog.eat()) // print "I'm a bulldog, and I don't eat."

// NORMAL FUNC
//print(Dog.sleep()) // compile error
print(dog.sleep()) // print "Zzz"
//print(Bulldog.sleep()) // compile error
print(bullDog.sleep()) // print "I'm a bulldog, and I don't sleep."
Nhon Nguyen
la source
7
Vos exemples ne couvrent pas le cas mentionné comme différence principale dans une autre réponse: répartition dynamique des classfonctions vs liaison statique de staticcelles-ci.
Jean-Philippe Pellet
1
Excellente explication pour comprendre les fonctions.
Yucel Bayram
34
N'est pas class funcremplaçable?
Iulian Onofrei
9
Si vous essayez de remplacer une méthode statique, vous obtiendrez une erreur. Cependant, vous pouvez remplacer une méthode de classe. Voir la réponse acceptée
Honey
8
class funcest remplaçable. J'aurais voté cela autrement; j'adore la recherche et l'exemple!
Ben Leggiero
52

Pour déclarer une propriété de variable de type, marquez la déclaration avec le staticmodificateur de déclaration. Les classes peuvent marquer les propriétés calculées de type avec le classmodificateur de déclaration à la place pour permettre aux sous-classes de remplacer l'implémentation de la superclasse. Les propriétés de type sont décrites dans les propriétés de type.

REMARQUE
Dans une déclaration de classe, le mot clé statica le même effet que le marquage de la déclaration avec les modificateurs de déclaration classet final.

Source: langage de programmation Swift - Propriétés des variables de type

NexD.
la source
5
La question porte sur «func statique» et «func de classe». Il ne s'agit PAS de propriétés de type. Cela ne répond donc pas à la question - bien qu'il soit important de comprendre également le contexte de ces mots clés en ce qui concerne les propriétés.
etayluz
Cette réponse est simplement sur la mauvaise question, peut-être a-t-elle été affichée ici accidentellement?
Fattie
15

Selon le livre Swift 2.2 publié par apple:

«Vous indiquez des méthodes de type en écrivant le staticmot - clé avant le mot-clé func de la méthode. Les classes peuvent également utiliser le classmot clé pour permettre aux sous-classes de remplacer l'implémentation de cette méthode par la superclasse . »

Milad
la source
10

Depuis Swift2.0, Apple dit:

"Toujours préfixer les exigences de propriété de type avec le mot clé statique lorsque vous les définissez dans un protocole. Cette règle s'applique même si les exigences de propriété de type peuvent être préfixées avec la classe ou le mot clé statique lorsqu'ils sont implémentés par une classe:"

Jacky
la source
5

Cet exemple effacera tous les aspects!

import UIKit

class Parent {
    final func finalFunc() -> String { // Final Function, cannot be redeclared.
        return "Parent Final Function."
    }

    static func staticFunc() -> String { // Static Function, can be redeclared.
        return "Parent Static Function."
    }

    func staticFunc() -> String { // Above function redeclared as Normal function.
        return "Parent Static Function, redeclared with same name but as non-static(normal) function."
    }

    class func classFunc() -> String { // Class Function, can be redeclared.
        return "Parent Class Function."
    }

    func classFunc() -> String { // Above function redeclared as Normal function.
        return "Parent Class Function, redeclared with same name but as non-class(normal) function."
    }

    func normalFunc() -> String { // Normal function, obviously cannot be redeclared.
        return "Parent Normal Function."
    }
}

class Child:Parent {

    // Final functions cannot be overridden.

    override func staticFunc() -> String { // This override form is of the redeclared version i.e: "func staticFunc()" so just like any other function of normal type, it can be overridden.
        return "Child Static Function redeclared and overridden, can simply be called Child Normal Function."
    }

    override class func classFunc() -> String { // Class function, can be overidden.
        return "Child Class Function."
    }

    override func classFunc() -> String { // This override form is of the redeclared version i.e: "func classFunc()" so just like any other function of normal type, it can be overridden.
        return "Child Class Function, redeclared and overridden, can simply be called Child Normal Function."
    }

    override func normalFunc() -> String { // Normal function, can be overridden.
        return "Child Normal Function."
    }
}

let parent = Parent()
let child = Child()

// Final
print("1. " + parent.finalFunc())   // 1. Can be called by object.
print("2. " + child.finalFunc())    // 2. Can be called by object, parent(final) function will be called.
// Parent.finalFunc()               // Cannot be called by class name directly.
// Child.finalFunc()                // Cannot be called by class name directly.

// Static
print("3. " + parent.staticFunc())  // 3. Cannot be called by object, this is redeclared version (i.e: a normal function).
print("4. " + child.staticFunc())   // 4. Cannot be called by object, this is override form redeclared version (normal function).
print("5. " + Parent.staticFunc())  // 5. Can be called by class name directly.
print("6. " + Child.staticFunc())   // 6. Can be called by class name direcly, parent(static) function will be called.

// Class
print("7. " + parent.classFunc())   // 7. Cannot be called by object, this is redeclared version (i.e: a normal function).
print("8. " + child.classFunc())    // 8. Cannot be called by object, this is override form redeclared version (normal function).
print("9. " + Parent.classFunc())   // 9. Can be called by class name directly.
print("10. " + Child.classFunc())   // 10. Can be called by class name direcly, child(class) function will be called.

// Normal
print("11. " + parent.normalFunc())  // 11. Can be called by object.
print("12. " + child.normalFunc())   // 12. Can be called by object, child(normal) function will be called.
// Parent.normalFunc()               // Cannot be called by class name directly.
// Child.normalFunc()                // Cannot be called by class name directly.

/*
 Notes:
 ___________________________________________________________________________
 |Types------Redeclare------Override------Call by object------Call by Class|
 |Final----------0--------------0---------------1------------------0-------|
 |Static---------1--------------0---------------0------------------1-------|
 |Class----------1--------------1---------------0------------------1-------|
 |Normal---------0--------------1---------------1------------------0-------|
 ---------------------------------------------------------------------------

 Final vs Normal function: Both are same but normal methods can be overridden.
 Static vs Class function: Both are same but class methods can be overridden.
 */

Production: Sortie tous types de fonctions

Rehan Ali Khan
la source
-6

C'est ce qu'on appelle les méthodes de type et elles sont appelées avec la syntaxe à points, comme les méthodes d'instance. Cependant, vous appelez des méthodes de type sur le type, pas sur une instance de ce type. Voici comment appeler une méthode de type sur une classe appelée SomeClass:

Kumar Utsav
la source
1
class SomeClass {class func someTypeMethod () {// l'implémentation de la méthode de type va ici}} SomeClass.someTypeMethod ()
Kumar Utsav
Cela ne répond pas du tout à la question. Il a demandé la différence entre les mots clés staticet class.
Doug McBride