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

Я вижу эти определения в библиотеке 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 для классов и протоколы? Есть ли другие различия, о которых следует знать? Каково обоснование наличия этого различия в самом синтаксисе?

7 ответов


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

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

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

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

мы рассматривали унификацию синтаксиса (например, использование "type" в качестве ключевого слова), но на самом деле это не просто вещи. Ключевые слова "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

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

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

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

TL; DR enter image description here

как вы можете видеть, в случае 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."

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

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

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


согласно Книге Swift 2.2, опубликованной apple:

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


Из Swift2.0, Apple говорит:

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


основное различие заключается в том, что структуры являются типами значений, а классы являются ссылочными типами.

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

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