Qual è la differenza tra funzioni statiche e funzioni di classe in Swift?


334

Vedo queste definizioni nella libreria Swift:

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

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

Qual è la differenza tra una funzione membro definita come static funce un'altra definita come class func? È semplicemente staticper funzioni statiche di strutture ed enumerazioni e classper classi e protocolli? Ci sono altre differenze che dovresti conoscere? Qual è la logica per avere questa distinzione nella sintassi stessa?


3
Non c'è differenza davvero. Non potevano usare la funzione di classe in una struttura immagino, quindi funzione statica. struct func sarebbe stato un buon candidato. Questo è un po 'spigoloso se me lo chiedi ma bene, queste sono le parole.
fabrice truillot de chambrier,

2
Domanda bonus, quindi: una struttura può conformarsi a un protocollo che definisce un class func? Con le informazioni che abbiamo ora, questa distinzione sembra piuttosto inutile, no?
Jean-Philippe Pellet,

3
si, puoi. Strano vero?
fabrice truillot de chambrier,

7
la differenza schiacciante è che puoi scavalcare class funcs
Fattie il

1
Da considerare:error: class methods are only allowed within classes; use 'static' to declare a static method
Gabriel Goncalves il

Risposte:


238

È semplicemente che statico è per funzioni statiche di strutture ed enumerazioni e classe per classi e protocolli?

Questa è la differenza principale. Alcune altre differenze sono che le funzioni di classe vengono inviate in modo dinamico e possono essere sovrascritte da sottoclassi.

I protocolli usano la parola chiave class, ma non esclude le strutture dall'implementazione del protocollo, ma usano semplicemente statico. La classe è stata scelta per i protocolli, quindi non ci dovrebbe essere una terza parola chiave per rappresentare statica o classe.

Da Chris Lattner su questo argomento:

Abbiamo preso in considerazione l'unificazione della sintassi (ad es. Utilizzando "type" come parola chiave), ma in realtà non si tratta semplicemente di cose. Le parole chiave "classe" e "statico" sono utili per la familiarità e sono piuttosto descrittive (una volta compreso come funzionano i metodi +) e aprono la porta per l'aggiunta di metodi potenzialmente statici alle classi. La stranezza principale di questo modello è che i protocolli devono scegliere una parola chiave (e abbiamo scelto "class"), ma a conti fatti è il giusto compromesso.

Ed ecco uno snippet che mostra alcuni dei comportamenti di override delle funzioni di 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

4
Ah, punto molto importante che le funzioni di classe vengono inviate in modo dinamico! Ma potresti fornire un esempio del genere? Dovresti scrivere il nome della classe da qualche parte, giusto? Quindi perché non scegliere staticamente l'implementazione di quella classe?
Jean-Philippe Pellet,

1
Un'altra domanda complementare: da dove hai preso la citazione?
Jean-Philippe Pellet,

la mia comprensione è che le funzioni di classe funzionano più o meno allo stesso modo dei metodi objc + sotto il cofano
Connor

1
Posso fornire un link di risposta più semplice qui? stackoverflow.com/questions/29636633/...
allenlinli

1
@ Jean-PhilippePellet Nell'esempio sopra ... se usi al static func myFunc()posto di class func myFuncte otterrai il seguente errore l: impossibile sovrascrivere il metodo statico . Perché? Perché è come se fosse contrassegnato da final. Per maggiori informazioni. Vedi la risposta di nextD di seguito. Inoltre x.dynamicTypeè stato ora sostituito contype(of:x)
Honey

246

Per essere più chiari, faccio un esempio qui,

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 è lo stesso di final class func

Perché lo è final, non possiamo ignorarlo nella sottoclasse come di seguito:

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

18
tu campione, ottima risposta .. stavo cercando questa differenza .. Jake !!
Abhimanyu Rathore,

5
Perfetto. Degno di nota.
Mehul,

5
Questo dovrebbe essere contrassegnato come la risposta corretta. Ordinato e pulito!
abhinavroy23

1
La migliore spiegazione! Questo mi ha portato a un altro dubbio. C'è qualche motivo esplicito per usare un 'class func'? Voglio dire, se usi semplicemente 'func' può anche essere sovrascritto allo stesso modo, quindi qual è la differenza?
Marcos Reboucas,

1
@MarcosReboucas se capisco correttamente la tua domanda, class funcè diverso dal normale funcsebbene entrambi possano essere ignorati. Ma funcè per un'istanza / oggetto e si class funcpuò accedere attraverso la classe comeClassA.classFunc()
Jake Lin

78

Ho fatto alcuni esperimenti nel parco giochi e ho ottenuto alcune conclusioni.

TL; DR inserisci qui la descrizione dell'immagine

Come puoi vedere, nel caso di class, l'uso di class funcostatic func è solo una questione di abitudine.

Esempio di parco giochi con spiegazione:

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

7
I tuoi esempi non trattano il caso menzionato come differenza principale in un'altra risposta: invio dinamico di classfunzioni rispetto a binding statico di staticquelle.
Jean-Philippe Pellet,

1
Grande spiegazione per la comprensione delle funzioni.
Yucel Bayram,

34
Non è class funcignorabile?
Iulian Onofrei,

9
Se si tenta di sovrascrivere un metodo statico, si otterrà un errore. Tuttavia è possibile sovrascrivere un metodo di classe. Vedi la risposta accettata
Miele

8
class funcè scavalcabile. Altrimenti l'avrei votato; amo la ricerca e l'esempio!
Ben Leggiero,

52

Per dichiarare una proprietà variabile di tipo, contrassegnare la dichiarazione con il staticmodificatore di dichiarazione. Le classi possono invece contrassegnare le proprietà calcolate del tipo con il classmodificatore di dichiarazione per consentire alle sottoclassi di sovrascrivere l'implementazione della superclasse. Le proprietà del tipo sono discusse in Proprietà del tipo.

NOTA
In una dichiarazione di classe, la parola chiavestatic ha lo stesso effetto di contrassegnare la dichiarazione con entrambi i modificatori di dichiarazione classe final.

Fonte: il linguaggio di programmazione Swift: digitare le proprietà delle variabili


5
La domanda è: "static func" e "class func". NON chiede informazioni sulle proprietà del tipo. Quindi questo non risponde alla domanda, anche se è importante capire il contesto di queste parole chiave anche per quanto riguarda le proprietà.
etayluz,

Questa risposta è semplicemente sulla domanda sbagliata, forse è stata pubblicata qui per caso?
Fattie

15

Secondo il libro Swift 2.2 pubblicato da Apple:

“Indichi i metodi di tipo scrivendo la staticparola chiave prima della parola chiave func del metodo. Le classi possono anche utilizzare la classparola chiave per consentire alle sottoclassi di sovrascrivere l'implementazione di tale metodo da parte della superclasse . "


10

Da Swift2.0, Apple afferma:

"Prefisso sempre i requisiti di proprietà del tipo con la parola chiave statica quando li definisci in un protocollo. Questa regola riguarda anche se i requisiti di proprietà del tipo possono essere preceduti dalla classe o dalla parola chiave statica quando implementati da una classe:"


5

Questo esempio cancella ogni aspetto!

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.
 */

Produzione: Emette tutti i tipi di funzione


-6

Questo si chiama metodo di tipo e viene chiamato con sintassi punto, come i metodi di istanza. Tuttavia, si chiamano i metodi di tipo sul tipo, non su un'istanza di quel tipo. Ecco come si chiama un metodo di tipo su una classe chiamata SomeClass:


1
class SomeClass {class func someTypeMethod () {// l'implementazione del metodo type va qui}} SomeClass.someTypeMethod ()
Kumar Utsav

Questo non risponde affatto alla domanda. Ha chiesto la differenza tra statice classparole chiave.
Doug McBride,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.