¿Cuál es la diferencia entre func estática y func de clase en Swift?

334

Puedo ver estas definiciones en la biblioteca Swift:

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

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

¿Cuál es la diferencia entre una función miembro definida como static funcy otra definida como class func? ¿Es simplemente staticpara funciones estáticas de estructuras y enumeraciones, y classpara clases y protocolos? ¿Hay alguna otra diferencia que uno debería saber? ¿Cuál es la razón para tener esta distinción en la sintaxis misma?

Jean-Philippe Pellet
fuente
3
No hay diferencia realmente. Supongo que no podrían usar la función de clase en una estructura, de ahí la función estática. struct func habría sido un buen candidato. Esto es un poco nervioso si me preguntas, pero bueno, esas son las palabras.
fabrice truillot de chambrier
2
Pregunta adicional, entonces: ¿puede una estructura ajustarse a un protocolo que define un class func? Con la información que tenemos ahora, esta distinción parece bastante inútil, ¿no?
Jean-Philippe Pellet
3
sí tu puedes. ¿No es extraño?
fabrice truillot de chambrier
77
la abrumadora diferencia es que puedes anular class funcs
Fattie
1
Para ser considerado:error: class methods are only allowed within classes; use 'static' to declare a static method
Gabriel Goncalves

Respuestas:

238

¿Es simplemente que static es para funciones estáticas de estructuras y enumeraciones, y class para clases y protocolos?

Esa es la principal diferencia. Algunas otras diferencias son que las funciones de clase se distribuyen dinámicamente y pueden ser anuladas por subclases.

Los protocolos usan la palabra clave class, pero no excluye las estructuras de implementar el protocolo, solo usan static en su lugar. Se eligió la clase para los protocolos, por lo que no tendría que haber una tercera palabra clave para representar estática o clase.

De Chris Lattner sobre este tema:

Consideramos unificar la sintaxis (p. Ej., Usar "tipo" como palabra clave), pero en realidad no se trata simplemente de cosas. Las palabras clave "clase" y "estática" son buenas para la familiaridad y son bastante descriptivas (una vez que comprenda cómo funcionan los métodos +), y abren la puerta para agregar métodos verdaderamente estáticos a las clases. La rareza principal de este modelo es que los protocolos tienen que elegir una palabra clave (y elegimos "clase"), pero en general es la compensación correcta.

Y aquí hay un fragmento que muestra algunos de los comportamientos de anulación de las funciones de clase:

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
fuente
44
¡Ajá, punto muy importante que las funciones de clase se envían dinámicamente! ¿Pero podrías dar un ejemplo así? Tendrías que escribir el nombre de la clase en algún lado, ¿verdad? Entonces, ¿por qué no elegir estáticamente la implementación de esa clase?
Jean-Philippe Pellet
1
Otra pregunta complementaria: ¿de dónde sacaste la cita?
Jean-Philippe Pellet
mi entendimiento es que las funciones de clase funcionan más o menos lo mismo que objc + métodos bajo el capó
Connor
1
¿Puedo proporcionar un enlace de respuesta más simple aquí? stackoverflow.com/questions/29636633/…
allenlinli
1
@ Jean-PhilippePellet En el ejemplo anterior ... si usa en static func myFunc()lugar de class func myFunc, obtendrá el siguiente error l: no se puede anular el método estático . ¿Por qué? Porque es como si hubiera sido marcado con final. Para más información. Vea la siguiente respuesta de D a continuación. También x.dynamicTypeahora ha sido reemplazado portype(of:x)
Honey
246

Para ser más claro, hago un ejemplo aquí,

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 es igual que final class func

Como es así final, no podemos anularlo en la subclase de la siguiente manera:

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
fuente
18
campeón, gran respuesta ... estaba buscando esta diferencia ... ¡Jake!
Abhimanyu Rathore
55
Perfecto. Impresionante.
Mehul
55
Esto debe marcarse como la respuesta correcta. ¡Limpio y ordenado!
abhinavroy23 01 de
1
¡La mejor explicación! Esto me llevó a otra duda. ¿Hay alguna razón explícita para usar una 'clase func'? Quiero decir, si solo usas 'func', también se puede anular de la misma manera, ¿cuál es la diferencia?
Marcos Reboucas
1
@MarcosReboucas si entiendo su pregunta correctamente, class funces diferente de lo normal, funcaunque ambos pueden ser anulados. Pero funces para una instancia / objeto y class funcse puede acceder a través de la clase comoClassA.classFunc()
Jake Lin
78

Hice algunos experimentos en el patio y obtuve algunas conclusiones.

TL; DR ingrese la descripción de la imagen aquí

Como puede ver, en el caso de class, el uso de class funco static funces solo una cuestión de hábito.

Ejemplo de patio de recreo con explicación:

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
fuente
77
Sus ejemplos no cubren el caso mencionado como la diferencia principal en otra respuesta: despacho dinámico de classfunciones versus enlace estático de staticunas.
Jean-Philippe Pellet
1
Gran explicación para entender las funciones.
Yucel Bayram
34
¿No es class funcreemplazable?
Iulian Onofrei
99
Si intenta anular un método estático, OBTENDRÁ UN ERROR. Sin embargo, puede anular un método de clase. Ver la respuesta aceptada
Honey
8
class funces invaluable Hubiera votado esto de otra manera; Me encanta la investigación y el ejemplo!
Ben Leggiero
52

Para declarar una propiedad de variable de tipo, marque la declaración con el staticmodificador de declaración. Las clases pueden marcar propiedades calculadas de tipo con el classmodificador de declaración en su lugar para permitir que las subclases anulen la implementación de la superclase. Las propiedades de tipo se analizan en Propiedades de tipo.

NOTA
En una declaración de clase, la palabra clave statictiene el mismo efecto que marcar la declaración con los modificadores classy finaldeclaración.

Fuente: El lenguaje de programación Swift - Propiedades de tipo variable

NexD.
fuente
55
La pregunta es acerca de 'func estática' y 'func de clase'. NO está preguntando sobre las propiedades de tipo. Por lo tanto, esto no responde la pregunta, aunque también es importante comprender el contexto de estas palabras clave con respecto a las propiedades.
etayluz
Esta respuesta es simplemente sobre la pregunta incorrecta, ¿tal vez fue publicada aquí accidentalmente?
Fattie
15

De acuerdo con el libro Swift 2.2 publicado por apple:

“Indicas los métodos de escritura escribiendo la staticpalabra clave antes de la palabra clave func del método. Las clases también pueden usar la classpalabra clave para permitir que las subclases anulen la implementación de la superclase de ese método ".

Milad
fuente
10

De Swift2.0, Apple dice:

"Siempre prefija los requisitos de propiedad de tipo con la palabra clave estática cuando los defina en un protocolo. Esta regla se aplica a pesar de que los requisitos de propiedad de tipo pueden tener como prefijo la clase o la palabra clave estática cuando se implementa por una clase:"

Jacky
fuente
5

¡Este ejemplo aclarará todos los aspectos!

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

Salida: Salida de todo tipo de funciones

Rehan Ali Khan
fuente
-6

Esto se llama métodos de tipo y se llama con sintaxis de puntos, como los métodos de instancia. Sin embargo, llama a los métodos de tipo en el tipo, no en una instancia de ese tipo. Así es como se llama a un método de tipo en una clase llamada SomeClass:

Kumar Utsav
fuente
1
class SomeClass {class func someTypeMethod () {// la implementación del método de tipo va aquí}} SomeClass.someTypeMethod ()
Kumar Utsav
Esto no responde a la pregunta en absoluto. Preguntó la diferencia entre staticy classpalabras clave.
Doug McBride