Swift native base class หรือ NSObject


105

ผมทดสอบออกบางswizzling ISAกับสวิฟท์และพบว่ามันทำงานเฉพาะเมื่อ NSObject เป็นซุปเปอร์ชั้น (โดยตรงหรือเพิ่มขึ้นต่อไป) หรือโดยการใช้ '@objc' ตกแต่ง มิฉะนั้นจะเป็นไปตามรูปแบบการจัดส่งแบบคงที่และแบบ vtable เช่น C ++

เป็นเรื่องปกติหรือไม่ที่จะกำหนดคลาส Swift โดยไม่มีคลาสพื้นฐานของ Cocoa / NSObject หากเป็นเรื่องที่ฉันกังวลนี่หมายถึงพลวัตของ Objective-C ที่กล่าวมาข้างต้นเช่นการสกัดกั้นวิธีการและการวิปัสสนาแบบรันไทม์

พฤติกรรมเวลาทำงานแบบไดนามิกนั่งอยู่ในหัวใจของคุณสมบัติเช่นสังเกตการณ์คุณสมบัติหลักข้อมูลที่มุมมอง Oriented Programming , อุดมศึกษาสั่งซื้อการส่งข้อความ , การวิเคราะห์และกรอบการเข้าสู่ระบบและอื่น ๆ

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

แต่ด้วยกฎ 95-5 ทั่วไป ( 95% ของประสิทธิภาพที่เพิ่มขึ้นมาจากการปรับแต่งโค้ด 5% ) มันสมเหตุสมผลหรือไม่ที่จะเริ่มต้นด้วยคุณสมบัติไดนามิกที่ทรงพลังและทำให้แข็งขึ้นเมื่อจำเป็น?


ที่เกี่ยวข้อง: Swift รองรับการเขียนโปรแกรมเชิงด้านหรือไม่? stackoverflow.com/a/24137487/404201
Jasper Blues

คำตอบ:


109

คลาส Swift ที่เป็นคลาสย่อยของ NSObject:

  • เป็นคลาส Objective-C เอง
  • ใช้objc_msgSend()สำหรับการโทรไปยังวิธีการ (ส่วนใหญ่)
  • จัดเตรียมข้อมูลเมตารันไทม์ของ Objective-C สำหรับการใช้งานเมธอด (ส่วนใหญ่)

คลาส Swift ที่ไม่ใช่คลาสย่อยของ NSObject:

  • เป็นคลาส Objective-C แต่ใช้วิธีการเพียงไม่กี่วิธีสำหรับความเข้ากันได้ของ NSObject
  • ไม่ใช้objc_msgSend()สำหรับการโทรไปยังวิธีการของพวกเขา (โดยค่าเริ่มต้น)
  • อย่าให้ข้อมูลเมตารันไทม์ Objective-C สำหรับการนำเมธอดไปใช้ (โดยค่าเริ่มต้น)

Subclassing NSObject ใน Swift ทำให้คุณมีความยืดหยุ่นในรันไทม์ Objective-C แต่ยังมีประสิทธิภาพของ Objective-C อีกด้วย การหลีกเลี่ยง NSObject สามารถปรับปรุงประสิทธิภาพได้หากคุณไม่ต้องการความยืดหยุ่นของ Objective-C

แก้ไข:

ด้วย Xcode 6 beta 6 แอตทริบิวต์แบบไดนามิกจะปรากฏขึ้น สิ่งนี้ช่วยให้เราสามารถสั่ง Swift ได้ว่าเมธอดควรใช้การจัดส่งแบบไดนามิกดังนั้นจึงจะรองรับการสกัดกั้น

public dynamic func foobar() -> AnyObject {
}

1
Swift ใช้การจัดส่งแบบใดแทน objc_msgSend มันคงที่?
บิล

12
Swift อาจใช้ objc_msgSend dispatch, virtual table dispatch, direct dispatch หรือ inlining
Greg Parker

คงที่: น้อยกว่า 1.1ns vtable 1.1ns, msg ส่ง 4.9ns . (ขึ้นอยู่กับฮาร์ดแวร์แน่นอน). . ดูเหมือนว่า Swift 'บริสุทธิ์' จะเป็นภาษาที่ยอดเยี่ยมสำหรับการเขียนโปรแกรมระดับระบบ แต่สำหรับแอปฉันไม่เต็มใจที่จะละทิ้งคุณสมบัติแบบไดนามิกแม้ว่าฉันจะมีความสุขถ้าพวกเขาย้ายไปที่คอมไพเลอร์ as-in Garbage Collection กับ ARC . . ฉันได้ยินมาว่าการจัดส่งแบบคงที่ช่วยให้สามารถทำนายสาขาได้ดีขึ้น (และมีประสิทธิภาพ) ในระบบมัลติคอร์ จริงหรือ?
Jasper Blues

"คลาส Swift ที่ไม่ใช่คลาสย่อยของ NSObject คือคลาส Objective-C" - คุณสามารถระบุลิงก์ไปยังตำแหน่งที่คุณพบที่ระบุไว้ได้หรือไม่?
Matt S.

1
ดังนั้นโดยสรุปฉันควรทำเฉพาะ NSObject ระดับย่อยใน Swift เท่านั้นหากฉันต้องการสื่อสารกับรหัส Objective C?
MobileMon

14

ฉันยังพบว่าหากใช้คลาส Swift ใน NSObject ฉันเห็นพฤติกรรมรันไทม์ที่ไม่คาดคิดซึ่งอาจซ่อนข้อบกพร่องในการเข้ารหัส นี่คือตัวอย่าง

ในตัวอย่างนี้ซึ่งเราไม่ได้ใช้ NSObject คอมไพลเลอร์ระบุข้อผิดพลาดใน testIncorrect_CompilerShouldSpot อย่างถูกต้องการรายงาน "... " MyClass "ไม่สามารถแปลงเป็น" MirrorDisposition "" ได้

class MyClass {
  let mString = "Test"

  func getAsString() -> String {
    return mString
  }

  func testIncorrect_CompilerShouldSpot() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject == myString) {
        // Do something
      }
  }

  func testCorrect_CorrectlyWritten() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject.getAsString() == myString) {
        // Do something
      }
  }
}

ในตัวอย่างนี้ที่เราใช้NSObjectคอมไพลเลอร์ไม่พบข้อผิดพลาดใน testIncorrect_CompilerShouldSpot:

class myClass : NSObject {
  let mString = "Test"

  func getAsString() -> String {
    return mString
  }

  func testIncorrect_CompilerShouldSpot() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject == myString) {
        // Do something
      }
  }

  func testCorrect_CorrectlyWritten() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject.getAsString() == myString) {
        // Do something
      }
  }
}

ฉันเดาว่าคุณธรรมเป็นเพียงพื้นฐานของ NSObject ที่คุณต้องทำจริงๆ!


1
ขอบคุณสำหรับข้อมูล.
Jasper Blues

13

ตามการอ้างอิงภาษาไม่มีข้อกำหนดสำหรับคลาสย่อยคลาสรูทมาตรฐานใด ๆ ดังนั้นคุณสามารถรวมหรือละเว้นซูเปอร์คลาสได้ตามต้องการ

โปรดทราบว่าการละเว้น superclass จากการประกาศคลาสจะไม่กำหนด superclass พื้นฐานโดยปริยายใด ๆ เป็นการกำหนดคลาสพื้นฐานซึ่งจะกลายเป็นรูทสำหรับลำดับชั้นคลาสอิสระได้อย่างมีประสิทธิภาพ

จากการอ้างอิงภาษา:

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

การพยายามอ้างอิงsuperจากคลาสที่ไม่มีซูเปอร์คลาส (เช่นคลาสฐาน) จะทำให้เวลาคอมไพล์มีข้อผิดพลาด

'super' members cannot be referenced in a root class

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

1
ขอบคุณ. การใช้การจัดส่งแบบคงที่และ vtable เหมาะสมสำหรับการเขียนโปรแกรมระบบหรือการปรับแต่งประสิทธิภาพ เพื่อความสอดคล้องกับ Cocoa ดั้งเดิมฉันคิดว่าไดนามิกควรเป็นค่าเริ่มต้น (เว้นแต่จะมีใครสามารถโน้มน้าวฉันเป็นอย่างอื่นได้ดังนั้นคำถามนี้)
Jasper Blues

1

ฉันเชื่อว่าข้อมูล Swift ส่วนใหญ่จะไม่เป็นobjcเช่นนั้น เฉพาะส่วนที่จำเป็นในการสื่อสารกับโครงสร้างพื้นฐานของ Objective C เท่านั้นที่จะถูกทำเครื่องหมายอย่างชัดเจนเช่นนี้

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


สำหรับฉันแล้วมันสมเหตุสมผลแล้วที่ทำให้ไดนามิกเป็นค่าเริ่มต้น (ผ่านการขยาย NSObject โดยใช้การตกแต่ง '@objc' หรือวิธีการอื่น ๆ ) ดูเหมือนว่าเมื่อไม่มีคลาสพื้นฐาน Swift จะสนับสนุนการจัดส่งแบบคงที่ / vtable ซึ่งทำงานได้ดีกว่า แต่ในกรณีส่วนใหญ่สิ่งนี้ไม่จำเป็น . (90% ของกำไรมาจากการปรับแต่งโค้ด 10%) ความหวังของฉันคือความสอดคล้องกับพลวัตของ Objective-C คือการเลือกไม่ใช้แทนที่จะเลือกเข้าร่วม
Jasper Blues

วิธีการออกแบบภาษาเป็นตัวเลือกในการเลือกใช้ เท่าที่เราทราบอาจเป็นอย่างดีว่าในอนาคต API ของระบบจะถูกเพิ่มที่ไม่ใช่ objc โดยกำเนิด ในระยะกลาง / ระยะยาวพวกเขาอาจย้ายไลบรารีที่มีอยู่ไปยังรหัสที่ไม่ใช่ objc ด้วยชั้นความเข้ากันได้แบบบางใน objc ที่เรียกเข้าไปในรหัสที่ไม่ใช่ objc เราก็ไม่รู้ เราอาจจะทำได้educated guessesในอีกไม่กี่ปีนับจากนี้ แต่ในขณะนี้มันเป็นเพียงเครื่องหมายคำถามที่ยิ่งใหญ่
ไฟล์อนาล็อก

@JasperBlues ฉันจะเถียงว่าไม่จำเป็นต้องมีพลวัตตลอดเวลาและฉันอยากจะมีประสิทธิภาพตามค่าเริ่มต้นจากนั้นเลือกใช้พฤติกรรมแบบไดนามิกที่จำเป็น สำหรับแต่ละคนฉันเดาเอง :)
Lance

@ แลนซ์อืมมม อาจจะ. ฉันยังคงกังวลเกี่ยวกับ: ผู้สังเกตการณ์, AOP, ทดสอบกรอบการเยาะเย้ย, กรอบการวิเคราะห์ ฯลฯ และสิ่งที่เกี่ยวกับประสิทธิภาพคือ 90% ของกำไรมาจากการปรับแต่ง 10% . นั่นคือเหตุผลของฉัน - เลือกรับกรณี 10% เหล่านั้น ฉันคิดว่า AOP จะเป็นเรื่องใหญ่สำหรับแอป iOS สำหรับองค์กร แต่สามารถทำได้โดยใช้เครื่องมือคอมไพเลอร์เหมือนใน C ++ . แน่นอนว่าเกมการคำนวณทางวิทยาศาสตร์กราฟิกและนักพัฒนาอื่น ๆ จะต้อนรับประสิทธิภาพที่มากขึ้น :)
Jasper Blues

1

ข้อมูลต่อไปนี้คัดลอกมาจาก Swift-eBook ของ Apple และให้คำตอบที่เหมาะสมสำหรับคำถามของคุณ:

การกำหนด Base-Class

คลาสใด ๆ ที่ไม่ได้รับมรดกจากคลาสอื่นเรียกว่าคลาสพื้นฐาน

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


ข้อมูลอ้างอิง

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Inheritance.html#//apple_ref/doc/uid/TP40014097-CH17-XID_251


-6

มันปกติดี. ดูเป้าหมายการออกแบบของ Swift: เป้าหมายคือการทำให้ปัญหาการเขียนโปรแกรมระดับใหญ่หายไป วิธีการ swizzling อาจไม่ใช่หนึ่งในสิ่งที่คุณต้องการทำกับ Swift


3
วิธีการ swizzling เป็นวิธีที่จะบรรลุรูปแบบการสกัดกั้นที่รันไทม์ หนึ่งในการใช้งานนี้คือการใช้ข้อกังวลแบบตัดขวางตามหลักการของ Aspect Oriented Programming การแจ้งเตือนของ Apple เองผู้สังเกตการณ์ทรัพย์สิน (ในตัวเพื่อความรวดเร็ว) ข้อมูลหลักทั้งหมดใช้การหมุนเพื่อให้ได้ผลดี . สิ่งหนึ่งที่ทำให้ผู้คนหลงใหลใน Objective-C แม้จะมีไวยากรณ์ที่ไม่ชัดเจนก็คือพลวัตนี้ซึ่งทำให้ง่ายต่อการจัดหาโซลูชันที่สวยงามเมื่อใดก็ตามที่ต้องการรูปแบบการสกัดกั้น . ในความเป็นจริงไม่มีกรอบ AOP อย่างเป็นทางการสำหรับ Objc เนื่องจากวัตถุดิบนั้นดีมาก
Jasper Blues

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

1
ใช่ แต่สิ่งเหล่านี้ทั้งหมดขึ้นอยู่กับการสกัดกั้นซึ่งสามารถทำได้สองวิธี: คอมไพล์ - ไทม์ (C ++) หรือรันไทม์ (Ruby, Objective-C, Java (ผ่าน asm, cglib, ApsectJ ฯลฯ )) ภาษาสมัยใหม่มักจะทำในขณะรันไทม์ ผู้คนชื่นชอบ Objective-C เนื่องจากมีพฤติกรรมเหมือนภาษาสมัยใหม่แม้ว่าจะเป็น codger แบบเก่าก็ตาม เช่นเดียวกับที่คุณพูดยิ่งสิ่งนี้ถูกรวมเข้ากับภาษามากเท่าไหร่ก็ยิ่งดีขึ้นเท่านั้น (หากทำได้ดี!) . แต่สำหรับตอนนี้เราสามารถเชื่อมต่อกับมรดกที่ Objc / Foundation มอบให้ได้ซึ่งเป็นเหตุผลว่าทำไมฉันจึงหวังว่าการขยาย NSObject จะกลายเป็นแนวทางปฏิบัติมาตรฐานสำหรับแอปในชีวิตประจำวันในอีกไม่กี่ปีข้างหน้า
Jasper Blues
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.