ข้อผิดพลาดในคลาส Swift: คุณสมบัติไม่เริ่มต้นที่การเรียก super.init


219

ฉันมีสองชั้นShapeและSquare

class Shape {
    var numberOfSides = 0
    var name: String
    init(name:String) {
        self.name = name
    }
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

class Square: Shape {
    var sideLength: Double

    init(sideLength:Double, name:String) {
        super.init(name:name) // Error here
        self.sideLength = sideLength
        numberOfSides = 4
    }
    func area () -> Double {
        return sideLength * sideLength
    }
}

ด้วยการใช้งานด้านบนฉันได้รับข้อผิดพลาด:

property 'self.sideLength' not initialized at super.init call
    super.init(name:name)

ทำไมฉันต้องตั้งค่าself.sideLengthก่อนโทรsuper.init?


ค่อนข้างแน่ใจว่าสิ่งนี้เกี่ยวข้องกับการเขียนโปรแกรมที่ดีไม่ใช่ข้อ จำกัด ทางเทคนิคที่แท้จริง ถ้า Shape จะเรียกฟังก์ชันที่ Square มี overriden Square อาจต้องการใช้ sideLength แต่ยังไม่ได้เริ่มต้น Swift อาจ จำกัด คุณไม่ให้ทำสิ่งนี้โดยบังเอิญโดยบังคับให้คุณเริ่มต้นอินสแตนซ์ของคุณก่อนที่จะเรียกคลาสพื้นฐาน
cwharris

3
ตัวอย่างในหนังสือไม่ดี คุณควรโทร super.init () ครั้งสุดท้ายเพื่อให้แน่ใจว่าคุณสมบัติทั้งหมดได้รับการเริ่มต้นอธิบายด้านล่าง
ปาสกาล

คำตอบ:


173

อ้างอิงจากภาษา Swift Programming ซึ่งตอบคำถามของคุณ:

“ คอมไพเลอร์ของ Swift ดำเนินการตรวจสอบความปลอดภัยที่เป็นประโยชน์สี่ประการเพื่อให้แน่ใจว่าการกำหนดค่าเริ่มต้นสองเฟสเสร็จสมบูรณ์โดยไม่มีข้อผิดพลาด:”

การตรวจสอบความปลอดภัย 1“ เครื่องมือกำหนดค่าเริ่มต้นที่กำหนดต้องตรวจสอบให้แน่ใจว่า“ คุณสมบัติที่แนะนำโดยคลาสนั้นถูกเตรียมใช้งานก่อนที่จะมอบสิทธิ์ให้กับซูเปอร์คลาสเริ่มต้น”

ข้อความที่ตัดตอนมาจาก: Apple Inc. “ ภาษาการเขียนโปรแกรม Swift” iBooks https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11


47
ตอนนี้เป็นการเปลี่ยนแปลงที่น่าประหลาดใจเมื่อเทียบกับ C ++, C # หรือ Java
MDJ

12
@ MDD แน่นอนค่ะ ฉันไม่เห็นมูลค่าเพิ่มของมันด้วยเช่นกัน
Ruben

20
ที่จริงดูเหมือนว่าจะมีอย่างใดอย่างหนึ่ง พูดใน C # ตัวสร้าง superclass ไม่ควรเรียกใช้เมธอด overridable ใด ๆ (เสมือน) เพราะไม่มีใครรู้ว่าพวกเขาจะตอบสนองอย่างไรกับ subclass ที่ไม่ได้เริ่มต้นอย่างสมบูรณ์ ใน Swift ก็โอเคเนื่องจาก subclass-extra state นั้นโอเคเมื่อตัวสร้าง superclass กำลังทำงาน ยิ่งไปกว่านั้นใน Swift all แต่วิธีสุดท้ายจะ overridable
MDJ

17
ฉันพบว่ามันน่ารำคาญเป็นพิเศษเพราะมันหมายถึงถ้าฉันต้องการสร้างคลาสย่อยของ UIView ซึ่งสร้าง subviews ของตัวเองฉันต้องเริ่มต้น subviews เหล่านั้นโดยไม่มีเฟรมเพื่อเริ่มต้นและเพิ่มเฟรมในภายหลังเนื่องจากฉันไม่สามารถอ้างถึงมุมมองของ ขอบเขตจนกระทั่งหลังจากเรียก super.init
Ash

5
@Janos ถ้าคุณทำให้ตัวเลือกสถานที่ที่คุณจะได้ไม่ต้อง initialise initใน
JeremyP

105

Swift มีลำดับของการดำเนินการที่ชัดเจนและเฉพาะเจาะจงซึ่งกระทำใน initializers มาเริ่มกันด้วยตัวอย่างพื้นฐานและหาทางไปสู่กรณีทั่วไป

ลองดูวัตถุ A เราจะนิยามดังนี้

class A {
    var x: Int
    init(x: Int) {
        self.x = x
    }
}

ขอให้สังเกตว่า A ไม่มีซูเปอร์คลาสดังนั้นจึงไม่สามารถเรียกใช้ฟังก์ชัน super.init () ได้เนื่องจากไม่มีอยู่

ตกลงตอนนี้เรามาเรียน subclass A พร้อมคลาสใหม่ชื่อ B

class B: A {
    var y: Int
    init(x: Int, y: Int) {
        self.y = y
        super.init(x: x)
    }
}

นี่คือการออกเดินทางจาก Objective-C ซึ่ง[super init]โดยทั่วไปจะเรียกก่อนสิ่งอื่นใด ไม่เช่นนั้นใน Swift คุณมีความรับผิดชอบในการตรวจสอบให้แน่ใจว่าตัวแปรอินสแตนซ์ของคุณอยู่ในสถานะที่สอดคล้องกันก่อนที่คุณจะทำสิ่งอื่นรวมถึงวิธีการโทร (ซึ่งรวมถึงการเริ่มต้น superclass


1
นี่เป็นประโยชน์อย่างมากตัวอย่างที่ชัดเจน ขอบคุณ!
FullMetalFist

ถ้าฉันต้องการค่าในการคำนวณ y เช่น: init (y: Int) {self.y = y * self.x super.init ()}
6rod9

1
ใช้บางอย่างเช่น init (y: Int, x: Int = 0) {self.y = y * x; self.x = x; super.init (x: x)}, นอกจากนี้คุณไม่สามารถเรียกตัวสร้างที่ว่างเปล่าโดยตรงสำหรับคลาสซูเปอร์ที่มีการอ้างอิงถึงตัวอย่างข้างต้นเนื่องจากชื่อซูเปอร์คลาส A ไม่มี
คอนสตรัคเตอร์

43

จากเอกสาร

ตรวจสอบความปลอดภัย 1

ตัวกำหนดค่าเริ่มต้นที่กำหนดต้องตรวจสอบให้แน่ใจว่าคุณสมบัติทั้งหมดที่แนะนำโดยคลาสนั้นถูกเตรียมใช้งานก่อนที่จะมอบสิทธิ์ให้กับตัวเริ่มต้น superclass


ทำไมเราต้องตรวจสอบความปลอดภัยเช่นนี้

เพื่อตอบคำถามนี้ให้ดำเนินการต่อแม้ว่ากระบวนการเตรียมใช้งานจะดำเนินไปอย่างรวดเร็ว

การเริ่มต้นสองเฟส

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

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

ดังนั้นเพื่อให้แน่ใจว่ากระบวนการกำหนดค่าเริ่มต้นสองขั้นตอนดำเนินการตามที่กำหนดไว้ข้างต้นมีการตรวจสอบความปลอดภัยสี่ประการโดยหนึ่งในนั้นคือ

ตรวจสอบความปลอดภัย 1

ตัวกำหนดค่าเริ่มต้นที่กำหนดต้องตรวจสอบให้แน่ใจว่าคุณสมบัติทั้งหมดที่แนะนำโดยคลาสนั้นถูกเตรียมใช้งานก่อนที่จะมอบสิทธิ์ให้กับตัวเริ่มต้น superclass

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

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

เหมือนในตัวอย่างนี้

class Shape {
    var name: String
    var sides : Int
    init(sides:Int, named: String) {
        self.sides = sides
        self.name = named
    }
}

class Triangle: Shape {
    var hypotenuse: Int
    init(hypotenuse:Int) {
        super.init(sides: 3, named: "Triangle") 
        self.hypotenuse = hypotenuse
    }
}

Triangle.initมีการเตรียมใช้งานคุณสมบัติทั้งหมดก่อนที่จะถูกใช้ ดังนั้นการตรวจสอบความปลอดภัย 1 ดูเหมือนไม่เกี่ยวข้อง

แต่อาจมีอีกสถานการณ์หนึ่งที่ซับซ้อนเล็กน้อย

class Shape {
    var name: String
    var sides : Int
    init(sides:Int, named: String) {
        self.sides = sides
        self.name = named
        printShapeDescription()
    }
    func printShapeDescription() {
        print("Shape Name :\(self.name)")
        print("Sides :\(self.sides)")
    }
}

class Triangle: Shape {
    var hypotenuse: Int
    init(hypotenuse:Int) {
        self.hypotenuse = hypotenuse
        super.init(sides: 3, named: "Triangle")
    }

    override func printShapeDescription() {
        super.printShapeDescription()
        print("Hypotenuse :\(self.hypotenuse)")
    }
}

let triangle = Triangle(hypotenuse: 12)

ผลผลิต:

Shape Name :Triangle
Sides :3
Hypotenuse :12

นี่ถ้าเราได้เรียกว่าsuper.initก่อนที่จะตั้งค่าhypotenuseการsuper.initโทรจากนั้นก็จะมีการเรียกและตั้งแต่ที่ได้รับการแทนที่มันจะเป็นครั้งแรกทางเลือกในการดำเนินการระดับสามเหลี่ยมprintShapeDescription() ของการเข้าถึงระดับสามเหลี่ยมไม่ใช่สถานที่ให้บริการที่เป็นตัวเลือกที่ยังคงไม่ได้รับการเริ่มต้นใช้งาน และสิ่งนี้ไม่ได้รับอนุญาตเนื่องจากการกำหนดค่าเริ่มต้นแบบสองเฟสจะป้องกันไม่ให้เข้าถึงค่าคุณสมบัติก่อนที่จะเริ่มต้นprintShapeDescription()printShapeDescription()hypotenuse

เพื่อให้แน่ใจว่าการกำหนดค่าเริ่มต้นสองเฟสเสร็จสิ้นตามที่กำหนดไว้จะต้องมีคำสั่งที่เฉพาะเจาะจงสำหรับการโทรsuper.initและนั่นคือหลังจากการเริ่มต้นคุณสมบัติทั้งหมดที่แนะนำโดยselfคลาสดังนั้นเราจึงต้องมีการตรวจสอบความปลอดภัย 1


1
คำอธิบายที่ดีทำไมต้องเพิ่มคำตอบที่ดีที่สุด
Guy Daher

ดังนั้นโดยทั่วไปคุณกำลังพูดว่าเนื่องจาก superclass init อาจเรียกใช้ฟังก์ชัน (overriden) ... ซึ่งฟังก์ชันนั้นเข้าถึงคุณสมบัติ subclasses จากนั้นเพื่อหลีกเลี่ยงการไม่มีค่าที่ตั้งไว้การเรียกใช้จะsuperต้องเกิดขึ้นหลังจากตั้งค่าทั้งหมดแล้ว ตกลงทำให้รู้สึก สงสัยว่า Objective-C ทำไปแล้วและทำไมคุณต้องโทรหาsuperก่อน?
ฮันนี่

โดยเฉพาะอย่างยิ่งสิ่งที่คุณกำลังชี้ให้เห็นจะเป็นที่คล้ายกันไปที่: วางprintShapeDescription() ก่อน ที่จะสร้างข้อผิดพลาดนี้:self.sides = sides; self.name = named; use of 'self' in method call 'printShapeDescription' before all stored properties are initializedข้อผิดพลาดของ OP ได้รับการลดความเป็นไปได้ของข้อผิดพลาดรันไทม์
ฮันนี่

ฉันใช้คำว่า 'เป็นไปได้' โดยเฉพาะเพราะถ้าprintShapeDescriptionเป็นฟังก์ชั่นที่ไม่ได้อ้างถึงselfนั่นคือมันเป็นสิ่งที่เหมือน `print (" nothing ") ดังนั้นจะไม่มีปัญหาใด ๆ (ถึงกระนั้นสำหรับคอมไพเลอร์ก็จะโยนข้อผิดพลาดเพราะมันไม่ได้เป็นสมาร์ทซุปเปอร์ )
27917 Honey

objc ก็ไม่ปลอดภัย สวิฟต์เป็นประเภทที่ปลอดภัยดังนั้นวัตถุที่ไม่ใช่ตัวเลือกจำเป็นต้องเป็น nonilil จริงๆ!
Daij-Djan

36

"super.init ()" ควรถูกเรียกหลังจากคุณเริ่มต้นตัวแปรอินสแตนซ์ทั้งหมดของคุณ

ในวิดีโอ "Intermediate Swift" ของ Apple (คุณสามารถค้นหาได้ในหน้าทรัพยากรวิดีโอของ Apple Developer https://developer.apple.com/videos/wwdc/2014/ ) ที่เวลาประมาณ 28:40 น. กล่าวอย่างชัดเจนว่าผู้เริ่มต้นทั้งหมดใน ซูเปอร์คลาสจะต้องเรียกว่าหลังจากที่คุณเริ่มต้นตัวแปรอินสแตนซ์ของคุณ

ใน Objective-C มันเป็นสิ่งที่ตรงกันข้าม ใน Swift เนื่องจากคุณสมบัติทั้งหมดจะต้องเริ่มต้นก่อนที่จะถูกนำมาใช้เราจึงต้องเริ่มต้นคุณสมบัติก่อน สิ่งนี้มีไว้เพื่อป้องกันการเรียกใช้ฟังก์ชันที่แทนที่จากเมธอด "init ()" ของคลาสซูเปอร์คลาสโดยไม่ต้องเริ่มต้นคุณสมบัติก่อน

ดังนั้นการดำเนินการของ "สแควร์" ควรจะ:

class Square: Shape {
    var sideLength: Double

    init(sideLength:Double, name:String) {
        self.sideLength = sideLength
        numberOfSides = 4
        super.init(name:name) // Correct position for "super.init()"
    }
    func area () -> Double {
        return sideLength * sideLength
    }
}

1
จะไม่มีวันคาดเดาได้ การเริ่มต้นด้วยสุดยอดควรเป็นข้อความที่มีกำปั้นเป็นสิ่งที่ฉันคิด !! ฮึ่ม ค่อนข้างเปลี่ยนแปลงด้วยความรวดเร็ว
mythicalcoder

ทำไมต้องมาหลังจากนี้ โปรดระบุเหตุผลทางเทคนิค
Masih

14

ขออภัยที่จัดรูปแบบน่าเกลียด เพียงแค่ใส่ตัวอักษรคำถามหลังจากการประกาศและทุกอย่างจะโอเค คำถามบอกคอมไพเลอร์ว่าค่าเป็นทางเลือก

class Square: Shape {
    var sideLength: Double?   // <=== like this ..

    init(sideLength:Double, name:String) {
        super.init(name:name) // Error here
        self.sideLength = sideLength
        numberOfSides = 4
    }
    func area () -> Double {
        return sideLength * sideLength
    }
}

Edit1:

มีวิธีที่ดีกว่าในการข้ามข้อผิดพลาดนี้ ตามความคิดเห็นของ jmaschad ไม่มีเหตุผลที่จะใช้ตัวเลือกในกรณีของคุณสาเหตุตัวเลือกไม่สะดวกในการใช้และคุณจะต้องตรวจสอบเสมอว่าตัวเลือกไม่ได้เป็นศูนย์ก่อนที่จะเข้าถึง ดังนั้นสิ่งที่คุณต้องทำคือการเริ่มต้นสมาชิกหลังจากการประกาศ:

class Square: Shape {
    var sideLength: Double=Double()   

    init(sideLength:Double, name:String) {
        super.init(name:name)
        self.sideLength = sideLength
        numberOfSides = 4
    }
    func area () -> Double {
        return sideLength * sideLength
    }
}

Edit2:

หลังจากสอง minuses ได้คำตอบนี้ฉันพบวิธีที่ดียิ่งขึ้น ถ้าคุณต้องการให้สมาชิกคลาสเริ่มต้นในตัวสร้างของคุณคุณต้องกำหนดค่าเริ่มต้นให้กับมันภายในตัวสร้างและก่อนการเรียก super.init () แบบนี้:

class Square: Shape {
    var sideLength: Double  

    init(sideLength:Double, name:String) {
        self.sideLength = sideLength   // <= before super.init call..
        super.init(name:name)
        numberOfSides = 4
    }
    func area () -> Double {
        return sideLength * sideLength
    }
}

ขอให้โชคดีในการเรียนรู้ Swift


สวิทช์เพียงและsuper.init(name:name) self.sideLength = sideLengthการประกาศsideLengthเป็นทางเลือกเป็นการเข้าใจผิดและแนะนำเพิ่มเติมเกี่ยวกับความยุ่งยากในภายหลังเมื่อคุณต้องบังคับให้แกะออกมา
Johannes Luong

ใช่นี่คือตัวเลือก ขอบคุณ
fnc12

คุณสามารถทำได้var sideLength: Doubleโดยไม่จำเป็นต้องกำหนดเป็นค่าเริ่มต้น
Jarsen

ถ้าฉันมีค่าคงที่เป็นตัวเลือกจริงๆ ฉันจะทำอย่างไรกับมัน? ฉันต้องเริ่มต้นในตัวสร้างหรือไม่? ฉันไม่เห็นว่าทำไมคุณต้องทำ แต่คอมไพเลอร์บ่นใน Swift 1.2
Van Du Tran

1
สมบูรณ์แบบ! วิธีแก้ปัญหาทั้ง 3 วิธีใช้งานได้ "?", "String ()" แต่ปัญหาสำหรับฉันคือฉันยังไม่ได้ 'กำหนด' หนึ่งในสถานที่ให้บริการและเมื่อฉันทำมันได้ผล! ขอบคุณเพื่อน
Naishta

9

swift บังคับให้คุณต้องกำหนดค่าเริ่มต้นให้กับสมาชิกทุกคนก่อนที่จะใช้งาน / อาจเคยถูกใช้ เนื่องจากมันไม่สามารถแน่ใจได้ว่าจะเกิดอะไรขึ้นเมื่อมีการเลี้ยวโค้งมันผิดพลาด: ปลอดภัยกว่าขออภัย


1
สิ่งนี้ไม่สมเหตุสมผล IMO เนื่องจากคลาสผู้ปกครองไม่ควรมีการเปิดเผยคุณสมบัติที่ประกาศในนั้นว่าเป็นเด็ก!
Andy Hin

1
ไม่เพียง แต่คุณสามารถแทนที่สิ่งต่างๆและเริ่มใช้ตัวเอง 'ก่อนที่จะทำมันสุดยอด'
Daij-Djan

คุณเห็นที่นี่และความคิดเห็นที่ตามมาไหม ฉันคิดว่าฉันกำลังพูดอย่างที่คุณกำลังพูดนั่นคือเราทั้งคู่กำลังบอกว่าคอมไพเลอร์ต้องการความปลอดภัยมากกว่าที่จะเสียใจด้วยคำถามเดียวของฉันคือดังนั้นวัตถุประสงค์ -c จึงแก้ปัญหานี้ได้อย่างไร หรือไม่ หากไม่เป็นเช่นนั้นทำไมคุณยังต้องเขียนsuper.initบรรทัดแรก
ฮันนี่

7

เอ็ดเวิร์ด

คุณสามารถแก้ไขรหัสในตัวอย่างของคุณเช่นนี้:

var playerShip:PlayerShip!
var deltaPoint = CGPointZero

init(size: CGSize)
{
    super.init(size: size)
    playerLayerNode.addChild(playerShip)        
}

นี่คือการใช้ตัวเลือกที่ยังไม่ได้เปิดโดยปริยาย

ในเอกสารเราสามารถอ่าน:

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


ฉันคิดว่านี่เป็นตัวเลือกที่สะอาดที่สุด ในความพยายามครั้งแรกอย่างรวดเร็วของฉันฉันมีตัวแปรสมาชิกประเภท AVCaptureDevice ซึ่งไม่สามารถสร้างอินสแตนซ์ได้โดยตรงดังนั้นจำเป็นต้องใช้รหัส init () อย่างไรก็ตาม ViewController ต้องการ initializers หลายตัวและคุณไม่สามารถเรียกวิธีการกำหนดค่าเริ่มต้นทั่วไปจาก init () ดังนั้นคำตอบนี้ดูเหมือนจะเป็นตัวเลือกเดียวที่หลีกเลี่ยงการคัดลอก / วางรหัสที่ซ้ำกันในทุก ๆ initializer
sunetos

6

Swift จะไม่อนุญาตให้คุณเริ่มต้นคลาส super โดยไม่ต้องเริ่มต้นคุณสมบัติย้อนกลับของ Obj C. ดังนั้นคุณต้องเริ่มต้นคุณสมบัติทั้งหมดก่อนที่จะเรียก "super.init"

โปรดไปที่http://blog.scottlogic.com/2014/11/20/swift-initialisation.html มันให้คำอธิบายที่ดีสำหรับปัญหาของคุณ


6

เพิ่มศูนย์เพื่อสิ้นสุดการประกาศ


// Must be nil or swift complains
var someProtocol:SomeProtocol? = nil

// Init the view
override init(frame: CGRect)
    super.init(frame: frame)
    ...

สิ่งนี้ใช้ได้กับกรณีของฉัน แต่อาจไม่เหมาะกับคุณ


Good หนึ่งในขณะที่ใช้กับ UIView กับ UIViewController ที่มีโปรโตคอล
abdul sathar

1

คุณกำลังอ้างถึงในลำดับที่ผิด

     class Shape2 {
        var numberOfSides = 0
        var name: String
        init(name:String) {
            self.name = name
        }
        func simpleDescription() -> String {
            return "A shape with \(numberOfSides) sides."
        }
    }

    class Square2: Shape2 {
        var sideLength: Double

        init(sideLength:Double, name:String) {

            self.sideLength = sideLength
            super.init(name:name) // It should be behind "self.sideLength = sideLength"
            numberOfSides = 4
        }
        func area () -> Double {
            return sideLength * sideLength
        }
    }

0

ฉันจะได้รับ downvotes บางอย่าง แต่ตามจริงแล้วชีวิตจะง่ายขึ้นด้วยวิธีนี้:

class CSListServerData<ListItem: CSJsonData>: CSServerData {
    var key: String!
    var type: ListItem.Type!
    var property: CSJsonDataList<ListItem>!

    func construct(_ key: String, _ type: ListItem.Type) -> Self {
        self.key = key
        self.type = type
        property = CSJsonDataList(self, type, key)
        return self
    }

    func construct(_ type: ListItem.Type) { construct("list", type) }

    var list: [ListItem] { property.list }
}

-4

มันเป็นการออกแบบที่โง่มาก

ลองพิจารณาสิ่งนี้:

.
.
.
var playerShip:PlayerShip
var deltaPoint = CGPointZero

init(size: CGSize)
{
    super.init(size: size)
    playerShip = PlayerShip(pos: CGPointMake(self.size.width / 2.0, 100))
    playerLayerNode.addChild(playerShip)        
}
.
.
.

สิ่งนี้ไม่ถูกต้องตามที่ระบุไว้ข้างต้น แต่เป็น:

.
.
.
var playerShip:PlayerShip = PlayerShip(pos: CGPointMake(self.size.width / 2.0, 100))
var deltaPoint = CGPointZero

init(size: CGSize)
{
    super.init(size: size)
    playerLayerNode.addChild(playerShip)        
}
.
.
.

เพราะ 'ตัวเอง' ยังไม่ได้เริ่มต้น

ฉันหวังเป็นอย่างยิ่งว่าข้อผิดพลาดนี้จะได้รับการแก้ไขในไม่ช้า

(ใช่ฉันรู้ว่าฉันสามารถสร้างวัตถุที่ว่างเปล่าแล้วกำหนดขนาด แต่นั่นเป็นเพียงโง่)


2
นี่ไม่ใช่ข้อผิดพลาดซึ่งเป็นพื้นฐานการเขียนโปรแกรมใหม่ของ OOPs
Hitendra Solanki
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.