В чем разница между статическим и классовым функциями в Swift?

334

Я вижу эти определения в библиотеке Swift:

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

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

В чем разница между функцией-членом, определенной static funcи другой, как class func? Это просто staticдля статических функций структур и перечислений, а также classдля классов и протоколов? Есть ли другие отличия, о которых нужно знать? В чем смысл такого различия в самом синтаксисе?

Жан-Филипп Пелле
источник
3
На самом деле нет никакой разницы. Я думаю, они не могли использовать класс func в структуре, следовательно, статический func. struct func была бы хорошим кандидатом. Это немного раздражает, если вы спросите меня, но это слова.
Фабрис Трульо де Шамбрие
2
Бонусный вопрос: может ли структура соответствовать протоколу, который определяет class func? С информацией, которой мы располагаем сейчас, это различие кажется довольно бесполезным, не так ли?
Жан-Филипп Пелле
3
Да, ты можешь. Странно, не правда ли?
Фабрис Трульо де Шамбрие
7
Подавляющая разница в том, что вы можете переопределить class funcs
Fattie
1
Для рассмотрения:error: class methods are only allowed within classes; use 'static' to declare a static method
Габриэль Гонсалвес

Ответы:

238

Это просто, что static для статических функций структур и перечислений, и class для классов и протоколов?

Это главное отличие. Некоторые другие различия заключаются в том, что функции класса отправляются динамически и могут быть переопределены подклассами.

Протоколы используют ключевое слово class, но оно не исключает структур из реализации протокола, они просто используют static. Класс был выбран для протоколов, поэтому не должно быть третьего ключевого слова для представления статического или класса.

От Криса Латтнера на эту тему:

Мы рассмотрели унификацию синтаксиса (например, используя «тип» в качестве ключевого слова), но на самом деле это не просто вещи. Ключевые слова «класс» и «статический» удобны для знакомства и достаточно описательны (как только вы поймете, как работают методы +) и открывают двери для потенциально добавления действительно статических методов в классы. Основная странность этой модели в том, что протоколы должны выбирать ключевое слово (а мы выбрали «класс»), но в итоге это правильный компромисс.

А вот фрагмент кода, демонстрирующий некоторые переопределенные функции функций класса:

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
Ага, очень важный момент, что функции класса динамически отправляются! Но не могли бы вы привести такой пример? Вы должны написать имя класса где-нибудь, верно? Так почему бы не выбрать статически реализацию этого класса?
Жан-Филипп Пелле
1
Еще один дополнительный вопрос: откуда вы взяли цитату?
Жан-Филипп Пелле
Насколько я понимаю, функции класса работают почти так же, как методы objc + под капотом
Коннор
1
Могу ли я предоставить более простую ссылку для ответа здесь? stackoverflow.com/questions/29636633/…
allenlinli
1
@ Jean-PhilippePellet В приведенном выше примере ... если вы используете static func myFunc()вместо class func myFuncвас, вы получите следующую ошибку l: не может переопределить статический метод . Зачем? Потому что это как если бы оно было отмечено final. Чтобы получить больше информации. Смотрите следующий ответ ниже. Также x.dynamicTypeтеперь был заменен наtype(of:x)
Мед
246

Чтобы быть более ясным, я приведу пример здесь,

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 такой же как final class func

Поскольку это так final, мы не можем переопределить его в подклассе, как показано ниже:

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
Вы чемпион, отличный ответ .. я искал эту разницу ... Джейк!
Абхиманью Раторе
5
Отлично. Впечатляет.
Мехул
5
Это должно быть помечено как правильный ответ. С иголочки!
abhinavroy23
1
Лучшее объяснение! Это привело меня к другому сомнению. Есть ли какая-либо явная причина использовать «классовую функцию»? Я имею в виду, что если вы просто используете 'func', он также может быть переопределен таким же образом, так в чем же разница?
Маркос Ребукас
1
@MarcosReboucas, если я правильно понимаю ваш вопрос, class funcотличается от обычного, funcхотя оба могут быть отменены. Но funcэто для экземпляра / объекта и class funcмогут быть доступны через класс, какClassA.classFunc()
Джейк Лин
78

Я провел несколько экспериментов на детской площадке и получил некоторые выводы.

TL; DR введите описание изображения здесь

Как вы можете видеть, в случае classиспользования class funcили static funcэто просто вопрос привычки.

Пример детской площадки с объяснением:

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
Ваши примеры не охватывают случай, упомянутый в качестве основного отличия в другом ответе: динамическое распределение classфункций или статическое связывание staticих.
Жан-Филипп Пелле
1
Отличное объяснение для понимания функций.
Юсель Байрам
34
Не class funcперезаписывается?
Юлиан Онофрей
9
Если вы попытаетесь переопределить статический метод, вы получите ошибку. Однако вы можете переопределить метод класса. Смотрите принятый ответ
Мед
8
class funcпереопределяется Я бы проголосовал за это иначе; люблю исследования и пример!
Бен Легжеро
52

Чтобы объявить свойство переменной типа, пометьте staticобъявление модификатором объявления. Классы могут пометить вычисляемые свойства типа classмодификатором объявления, чтобы подклассы могли переопределить реализацию суперкласса. Свойства типа обсуждаются в разделе Свойства типа.

Примечание
В объявлении класса, ключевое слово staticимеет тот же эффект , как и маркировка декларации с обеими classи finalдекларацией модификаторов.

Источник: Язык программирования Swift - Тип Переменные Свойства

NexD.
источник
5
Вопрос заключается в том, что такое «статическая функция» и «классовая функция». Это НЕ спрашивает о свойствах типа. Так что это не отвечает на вопрос - хотя важно понимать контекст этих ключевых слов и в отношении свойств.
Этаилуз
Этот ответ просто на неправильный вопрос, возможно, он был размещен здесь случайно?
Толстяк
15

Согласно Книге Swift 2.2, изданной Apple:

«Вы указываете методы типа, написав staticключевое слово перед ключевым словом func метода. Классы также могут использовать classключевое слово, чтобы подклассы могли переопределить реализацию этого метода суперклассом » .

Milad
источник
10

От Swift2.0 Apple говорит:

«Всегда определяйте префиксные требования к свойству типа при помощи ключевого слова static, когда вы определяете их в протоколе. Это правило имеет место, даже если требования к свойствам типа могут быть добавлены с префиксом class или static при реализации классом:»

Jacky
источник
5

Этот пример прояснит каждый аспект!

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

Вывод: Вывести все типы функций

Рехан Али Хан
источник
-6

Это называется типом методов и вызывается с точечным синтаксисом, как методы экземпляра. Однако вы вызываете методы типа для типа, а не для экземпляра этого типа. Вот как вы вызываете метод типа для класса SomeClass:

Кумар Утсав
источник
1
class SomeClass {class func someTypeMethod () {// здесь идет реализация метода типа}} SomeClass.someTypeMethod ()
Кумар Утсав,
Это не отвечает на вопрос вообще. Он спросил разницу между staticи classключевыми словами.
Даг МакБрайд,