เหตุใด Swift initialise subclass จะต้องมีฟิลด์ที่เหมาะสมเป็นอันดับแรก


9

ในภาษา Swift เพื่อเริ่มต้นอินสแตนซ์หนึ่งต้องกรอกข้อมูลในฟิลด์ทั้งหมดของคลาสนั้นและจากนั้นเรียก superconstructor เท่านั้น:

class Base {
    var name: String

    init(name: String) {
        self.name = name
    }
}

class Derived: Base {
    var number: Int

    init(name: String, number: Int) {
        // won't compile if interchange lines
        self.number = number
        super.init(name)
    }
}

สำหรับฉันดูเหมือนว่าย้อนหลังเนื่องจากต้องselfสร้างอินสแตนซ์ก่อนที่จะกำหนดค่าให้กับเขตข้อมูลและรหัสนั้นให้ความประทับใจราวกับว่าการผูกมัดเกิดขึ้นหลังจากการมอบหมายเท่านั้น นอกจากนั้นซูเปอร์คลาสยังไม่มีวิธีทางกฎหมายในการอ่านคุณสมบัติที่แนะนำของคลาสย่อยดังนั้นความปลอดภัยจะไม่นับในกรณีนี้

นอกจากนี้ภาษาอื่น ๆ อีกมากมายเช่น JavaScript และแม้แต่ Objective C ซึ่งเป็นบรรพบุรุษทางจิตวิญญาณของ Swift นั้นจำเป็นต้องมีการรับสายเรียกเข้าก่อนที่จะเข้าถึงselfไม่ใช่หลังจากนั้น

อะไรคือเหตุผลที่อยู่เบื้องหลังตัวเลือกนี้เพื่อกำหนดเขตข้อมูลที่จะกำหนดก่อนที่จะเรียก superconstructor?


น่าสนใจ มีข้อ จำกัด หรือไม่ที่สามารถวางการเรียกใช้เมธอด (โดยเฉพาะอย่างยิ่งเสมือน) เช่นพูดอย่างเดียวหลังจากผูกมัดได้หรือไม่? ใน C #, เขต subclass ของจะได้รับค่าเริ่มต้นแล้วผูกมัด / ก่อสร้าง superclass แล้วคุณสามารถเริ่มต้นพวกเขาจริง IIRC ..
เอริค Eidt

3
ประเด็นก็คือว่าคุณจะต้องเริ่มต้นทุกสาขาก่อนที่คุณจะช่วยให้ไม่ จำกัด selfการเข้าถึง
CodesInChaos

@ErikEidt: ใน Swift เฉพาะฟิลด์ตัวเลือกจะเริ่มต้นเป็นศูนย์โดยอัตโนมัติ
gnasher729

คำตอบ:


9

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

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

PS คุณพูดถึง Objective-C ใน Objective-C ทุกอย่างจะถูกกำหนดค่าเริ่มต้นเป็น 0 / nil / NO โดยอัตโนมัติ แต่ถ้าค่านั้นไม่ใช่ค่าที่ถูกต้องในการเริ่มต้นตัวแปรดังนั้นเมธอด init พื้นฐานสามารถเรียกเมธอดที่ถูกแทนที่ได้อย่างง่ายดายและใช้ตัวแปรที่ยังไม่ได้เริ่มต้นด้วยค่า 0 แทนค่าที่ถูกต้อง ใน Objective-C นั่นไม่ใช่การละเมิดกฎภาษา (นั่นคือวิธีการทำงาน) แต่เห็นได้ชัดว่ามีข้อบกพร่องในรหัสของคุณ ใน Swift ข้อผิดพลาดนั้นไม่ได้รับอนุญาตจากภาษา

PS มีความคิดเห็น "มันเป็นวัตถุที่ได้มาจากการเริ่มต้นหรือไม่เป็นที่สังเกตได้เนื่องจากกฎภาษา"? คลาส Derived ได้เริ่มสมาชิกของตัวเองก่อนที่จะเรียกเมธอด init พื้นฐานและสมาชิกที่ได้รับเหล่านี้จะเก็บค่าไว้ ดังนั้นไม่ว่าจะเป็นวัตถุที่ได้รับในเวลาที่เรียกว่าเริ่มต้นฐานหรือคอมไพเลอร์จะต้องทำอะไรบางอย่างที่ค่อนข้างแปลกประหลาด และหลังจากเมธอดฐานเริ่มต้นได้เตรียมใช้งานสมาชิกอินสแตนซ์ฐานทั้งหมดแล้วสามารถเรียกใช้ฟังก์ชันแทนที่ได้และจะพิสูจน์ได้ว่าเป็นอินสแตนซ์ของคลาสที่ได้รับมา


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

มันเป็นวัตถุที่ได้รับมาตั้งแต่ต้นหรือว่ากฎของภาษาทำให้สิ่งนั้นไม่สามารถสังเกตได้? ฉันคิดว่ามันเป็นรุ่นหลัง
Deduplicator

9

นี้มาจากกฎความปลอดภัยสวิฟท์ตามที่อธิบายไว้ในส่วนสองเฟสเริ่มต้นในเอกสารภาษาหน้าเริ่มต้น

ช่วยให้มั่นใจว่าทุกฟิลด์จะถูกตั้งค่าก่อนการใช้งาน (โดยเฉพาะพอยน์เตอร์, เพื่อหลีกเลี่ยงการล่ม)

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

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

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


แน่นอนว่ามันจะยากกว่านี้สำหรับ MI
Deduplicator

3

พิจารณา

  • เมธอดเสมือนที่กำหนดในคลาสพื้นฐานสามารถนิยามใหม่ในคลาสที่ได้รับ
  • ผู้รับเหมาของคลาสพื้นฐานอาจเรียกวิธีเสมือนนี้โดยตรงหรือโดยอ้อม
  • นิยามใหม่วิธีเสมือน (ในชั้นเรียนมา) อาจขึ้นอยู่กับค่าของเขตข้อมูลในการเป็นชุดชั้นมาอย่างถูกต้องในผู้รับเหมาชั้นเรียนมา
  • ผู้รับเหมาของคลาสที่ได้รับอาจเรียกวิธีการในคลาสพื้นฐานที่ขึ้นอยู่กับเขตข้อมูลที่ได้รับการตั้งค่าในผู้รับเหมาชั้นฐาน

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

หากคุณสามารถแก้ปัญหาเหล่านี้ได้อย่าผ่าน "ไป" ดำเนินการต่อโดยตรงกับคอลเล็กชัน PHd ของคุณ ...

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