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将是一个不错的选择。如果您问我,这有点前卫,但那是这些话。
truillot de chambrier织物

2
那么,还有一个额外的问题:一个结构体可以符合定义一个的协议class func吗?根据我们现在掌握的信息,这种区分似乎毫无用处,不是吗?
让-菲利普·佩莱

3
是的你可以。是不是很奇怪
fabrice truillot de chambrier 2014年

7
压倒性的差异是您可以覆盖class funcs
Fattie

1
要考虑的事项:error: class methods are only allowed within classes; use 'static' to declare a static method
加布里埃尔·贡萨尔维斯

Answers:


238

是否仅将static用于结构和枚举的静态函数,将class用于类和协议?

那是主要的区别。其他一些区别是类函数是动态调度的,并且可以被子类覆盖。

协议使用class关键字,但是它并未从实现协议中排除结构,而是仅使用static。为协议选择了类,因此不必使用第三个关键字来表示静态或类。

来自Chris Lattner的以下主题:

我们考虑统一语法(例如,使用“ 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

4
嗯,非常重要的一点是动态调度类函数!但是你能提供这样的例子吗?您必须将类名写在某个地方,对吗?那么为什么不静态选择该类的实现呢?
让-菲利普·佩莱

1
另一个补充问题:您从哪里获得报价?
让-菲利普·佩莱

我的理解是,类函数的工作原理与objc +方法完全相同
Connor

1
我可以在这里提供一个更简单的答案链接吗?stackoverflow.com/questions/29636633/…–
allenlinli

1
@ Jean-PhilippePellet在上面的示例中...如果使用static func myFunc()代替,class func myFunc则会出现以下错误l:无法覆盖static方法。为什么?因为就好像它被标记为final。欲获得更多信息。请参阅下面的nextD的答案。此外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
你冠军,很好的答案..我正在寻求这种差异..杰克!
Abhimanyu Rathore

5
完善。令人印象深刻。
Mehul

5
应该将其标记为正确答案。干净整洁!
abhinavroy23'1

1
最好的解释!这使我产生了另一个疑问。有没有明确的理由使用“类函数”?我的意思是,如果仅使用“ func”,也可以用相同的方式覆盖它,那么有什么区别?
马科斯·雷布卡斯

1
如果我正确理解您的问题,@ MarcosReboucas class func与正常情况有所不同,func尽管两者都可以被覆盖。但这func是针对实例/对象的,class func可以通过类进行访问ClassA.classFunc()
Jake Lin

78

我在操场上做了一些实验,并得出了一些结论。

TL; DR 在此处输入图片说明

如您所见,在中class,使用class funcstatic 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
Jean-Philippe Pellet

1
很好的理解功能的解释。
Yucel Bayram

33
不可class func替代吗?
Iulian Onofrei

9
如果您尝试覆盖静态方法,则会出现错误。但是,您可以覆盖类方法。查看已接受的答案
亲爱的

8
class func是可以覆盖的。否则我会投票赞成;喜欢研究和榜样!
Ben Leggiero

52

要声明类型变量属性,请使用static声明修饰符标记声明。类可以使用class声明修饰符标记类型计算的属性,以允许子类覆盖超类的实现。类型属性在类型属性中讨论。

注意
在类声明中,关键字static与使用classfinal修饰符标记声明的效果相同。

来源:Swift编程语言-类型变量属性


5
问题是关于“静态功能”和“类功能”的问题。它不是在问类型属性。因此,这并不能回答问题-尽管了解这些关键字在属性方面的上下文也很重要。
etayluz

这个答案只是在一个错误的问题上,也许是偶然地在这里发布了?
Fattie

15

根据苹果公司发布的Swift 2.2书:

“您可以通过static在方法的func关键字之前编写关键字来指示类型方法。类也可以使用class关键字来允许子类覆盖该方法的超类实现。”


10

苹果公司从Swift2.0中说:

“在协议中定义类型属性要求时,始终使用static关键字作为前缀。即使由类实现时,类型属性要求可以以class或static关键字作为前缀,该规则也适用:”


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()
Kumar Utsav

这根本无法回答问题。他问staticclass关键字之间的区别。
Doug McBride
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.