Jaka jest różnica między statycznym a klasowym w Swift?

334

Widzę te definicje w bibliotece Swift:

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

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

Jaka jest różnica między funkcją elementu zdefiniowaną jako static funca inną funkcją zdefiniowaną jako class func? Czy to po prostu staticdotyczy statycznych funkcji struktur i wyliczeń oraz classklas i protokołów? Czy są jakieś inne różnice, o których warto wiedzieć? Jakie jest uzasadnienie takiego rozróżnienia w samej składni?

Jean-Philippe Pellet
źródło
3
Naprawdę nie ma różnicy. Nie mogą używać func klasowego w strukturze, więc statyczny func. struct func byłby dobrym kandydatem. To trochę nerwowe, jeśli mnie pytasz, ale cóż, to są te słowa.
fabrice truillot de chambrier
2
Zatem pytanie dodatkowe: czy struktura może być zgodna z protokołem, który definiuje class func? Z informacji, które mamy teraz, to rozróżnienie wydaje się raczej bezużyteczne, prawda?
Jean-Philippe Pellet,
3
tak, możesz. Dziwne, prawda?
fabrice truillot de chambrier
7
przeważająca różnica polega na tym, że można zastąpić class funcs
Fattie
1
Należy wziąć pod uwagę:error: class methods are only allowed within classes; use 'static' to declare a static method
Gabriel Goncalves

Odpowiedzi:

238

Czy to po prostu, że statyczny służy do statycznych funkcji struktur i wyliczeń oraz klasy dla klas i protokołów?

To główna różnica. Niektóre inne różnice polegają na tym, że funkcje klas są wywoływane dynamicznie i mogą zostać zastąpione przez podklasy.

Protokoły używają słowa kluczowego class, ale nie wyklucza to, że struktury implementują protokół, po prostu używają static. Do protokołów wybrano klasę, więc nie byłoby trzeciego słowa kluczowego reprezentującego statyczność lub klasę.

Od Chrisa Lattnera na ten temat:

Rozważaliśmy ujednolicenie składni (np. Użycie słowa „type” jako słowa kluczowego), ale tak naprawdę nie jest to po prostu rzecz. Słowa kluczowe „klasa” i „statyczny” są dobre dla znajomości i są dość opisowe (gdy zrozumiesz, jak działają metody +) i otwierają drzwi do potencjalnie dodawania prawdziwie statycznych metod do klas. Podstawową dziwnością tego modelu jest to, że protokoły muszą wybrać słowo kluczowe (i wybraliśmy „klasę”), ale w sumie jest to właściwy kompromis.

A oto fragment kodu, który pokazuje niektóre z funkcji nadpisywania funkcji klasowych:

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
źródło
4
Aha, bardzo ważny punkt, że funkcje klas są dynamicznie wywoływane! Ale czy możesz podać taki przykład? Musiałbyś gdzieś wpisać nazwę klasy, prawda? Dlaczego więc nie wybrać statycznie implementacji tej klasy?
Jean-Philippe Pellet,
1
Kolejne pytanie uzupełniające: skąd masz wycenę?
Jean-Philippe Pellet,
rozumiem, że funkcje klasowe działają prawie tak samo jak metody objc + pod maską
Connor
1
Czy mogę podać tutaj prostszy link do odpowiedzi? stackoverflow.com/questions/29636633/…
allenlinli 9.04.16
1
@ Jean-PhilippePellet W powyższym przykładzie ... jeśli użyjesz static func myFunc()zamiast tego class func myFuncpojawi się następujący błąd l: nie można przesłonić metody statycznej . Dlaczego? Ponieważ to tak, jakby zostało oznaczone final. Po więcej informacji. Zobacz odpowiedź nextD poniżej. Również x.dynamicTypezostał zastąpionytype(of:x)
Miód
246

Aby być bardziej zrozumiałym, robię tutaj przykład,

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 jest taki sam jak final class func

Ponieważ tak jest final, nie możemy zastąpić go w podklasie, jak poniżej:

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
źródło
18
mistrzu, świetna odpowiedź ... szukałem tej różnicy ... Jake !!
Abhimanyu Rathore,
5
Doskonały. Imponujący.
Mehul
5
To powinno być oznaczone jako poprawna odpowiedź. Schludny i czysty!
abhinavroy23
1
Najlepsze wytłumaczenie! Doprowadziło mnie to do kolejnej wątpliwości. Czy jest jakiś wyraźny powód, aby używać „func class”? Mam na myśli to, że jeśli użyjesz po prostu „func”, można go również zastąpić w ten sam sposób, więc jaka jest różnica?
Marcos Reboucas
1
@MarcosReboucas, jeśli dobrze rozumiem twoje pytanie, class funcróżni się od normalnego, funcchociaż oba można zastąpić. Ale funcjest dla instancji / obiektu i class funcmożna uzyskać do niej dostęp za pośrednictwem klasy, takiej jakClassA.classFunc()
Jake Lin
78

Przeprowadziłem eksperymenty na placu zabaw i wyciągnąłem wnioski.

TL; DR wprowadź opis zdjęcia tutaj

Jak widać, w przypadku classużycia class funclub static funcjest to tylko kwestia przyzwyczajenia.

Przykład placu zabaw z wyjaśnieniem:

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
źródło
7
Wasze przykłady nie obejmują przypadku wymienionego jako główna różnica w innej odpowiedzi: dynamiczne wysyłanie classfunkcji vs. statyczne wiązanie staticjednych.
Jean-Philippe Pellet
1
Świetne wyjaśnienie dla zrozumienia funkcji.
Yucel Bayram
33
Czy nie można go class funczastąpić?
Iulian Onofrei
9
Jeśli spróbujesz zastąpić metodę statyczną, BĘDZIESZ BŁĄD. Jednak ty może zastąpić metody klasy. Zobacz zaakceptowaną odpowiedź
Honey,
8
class funcjest nadrzędne. W przeciwnym razie głosowałbym za tym; uwielbiam badania i przykład!
Ben Leggiero,
52

Aby zadeklarować właściwość zmiennej typu, zaznacz deklarację za pomocą staticmodyfikatora deklaracji. Klasy mogą classzamiast tego oznaczać właściwości obliczane typu za pomocą modyfikatora deklaracji, aby umożliwić podklasom zastąpienie implementacji nadklasy. Właściwości typu są omówione we właściwościach typu.

UWAGA
W deklaracji klasy słowo kluczowe staticma taki sam efekt jak oznaczenie deklaracji zarówno modyfikatorami deklaracji, jak classi finaldeklaracji.

Źródło: Swift Programming Language - Właściwości zmiennych typu

NexD.
źródło
5
Pytanie dotyczy funkcji „static func” i „class func”. NIE pyta o właściwości typu. To nie odpowiada na pytanie - choć ważne jest, aby zrozumieć kontekst tych słów kluczowych również w odniesieniu do właściwości.
etayluz
Ta odpowiedź jest po prostu niewłaściwym pytaniem, być może została tu przypadkowo opublikowana?
Fattie
15

Według książki Swift 2.2 opublikowanej przez Apple:

„Wskazujesz metody pisania, pisząc static słowo kluczowe przed słowem kluczowym func metody. Klasy mogą również używać classsłowa kluczowego, aby umożliwić podklasom zastąpienie implementacji tej metody przez nadklasę . ”

Milad
źródło
10

Od Swift2.0 Apple mówi:

„Zawsze definiuj wymagania właściwości typu przedrostkiem słowem kluczowym static podczas definiowania ich w protokole. Ta reguła dotyczy nawet tego, że wymagania właściwości typu mogą być poprzedzone słowem kluczowym class lub static, jeśli zostaną zaimplementowane przez klasę:”

Jacky
źródło
5

Ten przykład wyczyści każdy aspekt!

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

Wynik: Wyprowadza wszystkie typy funkcji

Rehan Ali Khan
źródło
-6

Nazywa się to metodami typu i są wywoływane za pomocą składni kropkowej, podobnie jak metody instancji. Jednak metody typu wywołuje się na typie, a nie na instancji tego typu. Oto jak wywołujesz metodę typu w klasie o nazwie SomeClass:

Kumar Utsav
źródło
1
klasa SomeClass {class func someTypeMethod () {// implementacja metody typu idzie tutaj}} SomeClass.someTypeMethod ()
Kumar Utsav
To wcale nie odpowiada na pytanie. Zapytał o różnicę między słowami kluczowymi statica class.
Doug McBride