อะไรคือข้อแตกต่างระหว่าง static func กับ class func ใน 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ฟังก์ชั่นคงที่ของ structs และ enums และclassสำหรับชั้นเรียนและโปรโตคอล? มีความแตกต่างอื่น ๆ ที่เราควรรู้หรือไม่? เหตุผลในการมีความแตกต่างนี้ในไวยากรณ์ของตัวเองคืออะไร?


3
ไม่มีความแตกต่างจริงๆ พวกเขาไม่สามารถใช้คลาส func ใน struct ฉันเดาดังนั้นคง func struct func น่าจะเป็นตัวเลือกที่ดี นี่มันค่อนข้างหงุดหงิดถ้าคุณถามฉัน แต่นั่นเป็นคำพูด
fabrice truillot de chambrier

2
คำถามโบนัสจากนั้น: โครงสร้างสามารถสอดคล้องกับโปรโตคอลที่กำหนดclass func? ด้วยข้อมูลที่เรามีตอนนี้ความแตกต่างนี้ค่อนข้างไร้ประโยชน์ใช่ไหม
Jean-Philippe Pellet

3
ใช่คุณสามารถ. แปลกใช่มั้ย
fabrice truillot de chambrier

7
ความแตกต่างที่ท่วมท้นคือคุณสามารถแทนที่class funcs
Fattie

1
ได้รับการพิจารณา:error: class methods are only allowed within classes; use 'static' to declare a static method
กาเบรียล Goncalves

คำตอบ:


238

มันเป็นเพียงแค่ว่าคงเป็นฟังก์ชั่นคงที่ของ structs และ enums และชั้นเรียนสำหรับชั้นเรียนและโปรโตคอล?

นั่นคือความแตกต่างที่สำคัญ ความแตกต่างอื่น ๆ คือฟังก์ชันคลาสจะถูกส่งแบบไดนามิกและสามารถแทนที่ได้ด้วยคลาสย่อย

โปรโตคอลใช้คีย์เวิร์ดของคลาส แต่ไม่ได้ยกเว้น structs จากการนำโพรโทคอลไปใช้พวกเขาเพียง แต่ใช้สแตติกแทน Class ถูกเลือกสำหรับโปรโตคอลดังนั้นจึงไม่จำเป็นต้องมีคีย์เวิร์ดที่สามเพื่อแทนสแตติกหรือคลาส

จาก 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
อ๊ะจุดสำคัญมากที่ฟังก์ชั่นคลาสนั้นถูกส่งแบบไดนามิก! แต่คุณสามารถให้ตัวอย่างเช่น คุณต้องเขียนชื่อคลาสที่ไหนสักแห่งใช่ไหม? ดังนั้นทำไมไม่เลือกการใช้งานของคลาสนั้นแบบคงที่?
Jean-Philippe Pellet

1
คำถามเพิ่มเติมอีกข้อหนึ่ง: คุณได้รับการเสนอราคาจากที่ไหน
Jean-Philippe Pellet

ความเข้าใจของผมคือว่าฟังก์ชั่นการทำงานระดับสวยมากเช่นเดียวกับ ObjC + วิธีการภายใต้ประทุน
คอนเนอร์

1
ฉันสามารถให้ลิงค์คำตอบที่ง่ายกว่านี้ได้ไหม stackoverflow.com/questions/29636633/…
allenlinli

1
@ Jean-PhilippePellet ในตัวอย่างข้างต้น ... ถ้าคุณใช้static func myFunc()แทนของclass func myFuncคุณจะได้รับ L ข้อผิดพลาดต่อไปนี้: ไม่สามารถแทนที่วิธีแบบคงที่ ทำไม? 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 23

1
คำอธิบายที่ดีที่สุด! สิ่งนี้ทำให้ฉันสงสัยอีกครั้ง มีเหตุผลใดที่ชัดเจนในการใช้ 'class func' หรือไม่ ฉันหมายความว่าถ้าคุณใช้ 'func' มันก็สามารถแทนที่ได้ด้วยวิธีเดียวกันดังนั้นความแตกต่างคืออะไร
Marcos Reboucas

1
@MarcosReboucas ถ้าฉันเข้าใจคำถามของคุณถูกต้องclass funcจะแตกต่างจากปกติfuncแม้ว่าทั้งสองจะถูกแทนที่ แต่funcสำหรับอินสแตนซ์ / วัตถุและclass funcสามารถเข้าถึงได้ผ่านคลาสเช่นClassA.classFunc()
Jake Lin

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ที่
Jean-Philippe Pellet

1
คำอธิบายที่ดีสำหรับฟังก์ชั่นการทำความเข้าใจ
Yucel Bayram

33
ไม่class funcoverridable?
Iulian Onofrei

9
หากคุณพยายามแทนที่วิธีคงที่คุณจะได้รับข้อผิดพลาด อย่างไรก็ตามคุณสามารถแทนที่วิธีการเรียน ดูคำตอบที่ยอมรับ
ฮั

8
class funcoverridable ฉันจะโหวตให้เป็นอย่างนี้ รักการวิจัยและตัวอย่าง!
Ben Leggiero

52

หากต้องการประกาศคุณสมบัติตัวแปรชนิดให้ทำเครื่องหมายการประกาศด้วยstaticตัวแก้ไขการประกาศ คลาสอาจทำเครื่องหมายคุณสมบัติที่คำนวณได้ของชนิดด้วยโมดิclassฟายเออร์การประกาศแทนเพื่ออนุญาตให้คลาสย่อยแทนที่การใช้งานของซูเปอร์คลาส คุณสมบัติของประเภทจะกล่าวถึงในคุณสมบัติของประเภท

หมายเหตุ
ในการประกาศคลาสคำสำคัญstaticมีผลเช่นเดียวกับการทำเครื่องหมายการประกาศที่มีทั้งตัวดัดแปลงclassและfinalการประกาศ

ที่มา: ภาษาการเขียนโปรแกรม Swift - คุณสมบัติตัวแปรชนิด


5
คำถามกำลังถามเกี่ยวกับ 'static func' และ 'class func' มันไม่ได้ถามเกี่ยวกับคุณสมบัติของประเภท ดังนั้นสิ่งนี้จึงไม่ตอบคำถาม - แม้ว่าจะต้องเข้าใจบริบทของคำหลักเหล่านี้เกี่ยวกับคุณสมบัติเช่นกัน
etayluz

คำตอบนี้เป็นเพียงคำถามที่ผิดบางทีมันถูกโพสต์ที่นี่โดยไม่ตั้งใจ
Fattie

15

อ้างอิงจาก Swift 2.2 Book ที่ตีพิมพ์โดย apple:

“ คุณระบุวิธีการพิมพ์โดยการเขียนstaticคำสำคัญหน้าคำหลัก func ของวิธีการ คลาสอาจใช้classคำสำคัญเพื่ออนุญาตให้คลาสย่อยแทนที่การใช้วิธีดังกล่าวของซูเปอร์คลาส


10

จาก Swift2.0, Apple พูดว่า:

"ข้อกำหนดคุณสมบัติของส่วนนำหน้าเสมอด้วยคีย์เวิร์ดแบบสแตติกเมื่อคุณกำหนดไว้ในโปรโตคอลกฎนี้เกี่ยวข้องแม้ว่าข้อกำหนดคุณสมบัติของประเภทสามารถนำหน้าด้วยคลาสหรือคีย์เวิร์ดแบบสแตติกเมื่อใช้งานโดยคลาส:"


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

สิ่งนี้เรียกว่าเมธอด type และถูกเรียกด้วยจุดไวยากรณ์เช่นเมธอดอินสแตนซ์ อย่างไรก็ตามคุณเรียกใช้เมธอดชนิดกับชนิดไม่ใช่ในอินสแตนซ์ของชนิดนั้น นี่คือวิธีที่คุณเรียกใช้เมธอด type บนคลาสที่ชื่อว่า SomeClass:


1
class SomeClass {class func someTypeMethod () {// การใช้งานเมธอดไปที่นี่}} SomeClass.someTypeMethod ()
Kumar Utsav

นี่ไม่ได้ตอบคำถามเลย เขาถามถึงความแตกต่างระหว่างstaticและclassคำหลัก
Doug McBride
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.