ฉันจะทำให้การอ้างอิงโปรโตคอลที่อ่อนแอใน 'บริสุทธิ์' Swift (ไม่มี @objc) ได้อย่างไร


561

weakดูเหมือนว่าการอ้างอิงจะไม่ทำงานใน Swift ยกเว้นว่ามีการprotocolประกาศเป็น@objcซึ่งฉันไม่ต้องการในแอพ Swift ที่บริสุทธิ์

รหัสนี้ให้ข้อผิดพลาดในการคอมไพล์ ( weakไม่สามารถใช้กับประเภทที่ไม่ใช่คลาสMyClassDelegate):

class MyClass {
  weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate {
}

ฉันต้องนำหน้าโปรโตคอลด้วย@objcแล้วก็ใช้งานได้

คำถาม: อะไรคือวิธีที่รวดเร็ว 'บริสุทธิ์' ในการบรรลุเป้าหมายweak delegate?


โปรดทราบ ... stackoverflow.com/a/60837041/294884
Fattie

คำตอบ:


1038

AnyObjectคุณจำเป็นต้องประกาศชนิดของโปรโตคอลเป็น

protocol ProtocolNameDelegate: AnyObject {
    // Protocol stuff goes here
}

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}

การใช้AnyObjectคุณบอกว่าคลาสเท่านั้นที่สามารถปฏิบัติตามโปรโตคอลนี้ได้ในขณะที่ struct หรือ enums ไม่สามารถทำได้


25
ปัญหาของฉันเกี่ยวกับวิธีแก้ไขปัญหานี้คือการเรียกผู้รับมอบสิทธิ์ทำให้เกิดความผิดพลาด - EXC_BAD_ACCESS (ดังที่บันทึกไว้โดยที่อื่น ๆ ) นี่น่าจะเป็นบั๊ก วิธีแก้ปัญหาเดียวที่ฉันพบคือใช้ @objc และกำจัดข้อมูล Swift ทั้งหมดจากโปรโตคอล
Jim T

12
วิธีที่ถูกต้องในการทำผู้แทนที่อ่อนแอใน Swift คืออะไร เอกสารของ Apple ไม่แสดงหรือประกาศผู้รับมอบสิทธิ์อ่อนแอในตัวอย่างรหัสของพวกเขา: developer.apple.com/library/ios/documentation/swift/conceptual/
......

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

5
BTW: ฉันคิดว่า "รูปแบบใหม่" (Swift 5) เป็นสิ่งที่ต้องทำprotocol ProtocolNameDelegate: AnyObjectแต่ไม่สำคัญ
hnh

1
มันควรจะเป็นAnyObjectเพราะclassจะถูกคัดค้านในบางจุด
José

283

คำตอบเพิ่มเติม

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

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

  • คุณจะต้องใช้เฉพาะในweakกรณีที่ผู้รับมอบสิทธิ์เป็นคลาส Swift structs และ enums เป็นประเภทค่า (ค่าจะถูกคัดลอกเมื่อสร้างอินสแตนซ์ใหม่) ไม่ใช่ประเภทการอ้างอิงดังนั้นจึงไม่ทำให้เกิดรอบการอ้างอิงที่แข็งแกร่ง

  • weakการอ้างอิงเป็นทางเลือกเสมอ (มิฉะนั้นคุณจะใช้unowned) และใช้เสมอvar(ไม่let) เพื่อให้ตัวเลือกสามารถตั้งค่าเป็นnilเมื่อมีการจัดสรรคืน

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

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

  • ตามกฎทั่วไปผู้ได้รับมอบหมายควรถูกทำเครื่องหมายว่าเป็นweakเพราะผู้ได้รับมอบหมายส่วนใหญ่อ้างอิงคลาสที่พวกเขาไม่ได้เป็นเจ้าของ สิ่งนี้เป็นจริงอย่างแน่นอนเมื่อเด็กใช้ผู้รับมอบสิทธิ์ในการสื่อสารกับผู้ปกครอง การใช้การอ้างอิงที่อ่อนแอสำหรับผู้รับมอบสิทธิ์เป็นสิ่งที่เอกสารแนะนำ ( แต่เห็นนี้ , เกินไป.)

  • โปรโตคอลสามารถใช้สำหรับทั้งประเภทการอ้างอิง (คลาส) และประเภทค่า (structs, enums) ดังนั้นในกรณีที่คุณต้องทำให้ผู้รับมอบสิทธิ์อ่อนแอคุณจะต้องทำให้มันเป็นโปรโตคอลเฉพาะวัตถุ วิธีที่จะทำคือการเพิ่มลงAnyObjectในรายการการสืบทอดของโปรโตคอล (ก่อนหน้านี้คุณทำสิ่งนี้โดยใช้classคำหลัก แต่AnyObjectเป็นที่ต้องการตอนนี้ )

    protocol MyClassDelegate: AnyObject {
        // ...
    }
    
    class SomeClass {
        weak var delegate: MyClassDelegate?
    }

การศึกษาต่อ

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

ที่เกี่ยวข้อง


5
ทั้งหมดนี้เป็นสิ่งที่ดีและน่าสนใจ แต่ไม่เกี่ยวข้องกับคำถามดั้งเดิมของฉัน - ซึ่งไม่เกี่ยวกับความอ่อนแอ / ARC เองหรือเกี่ยวกับสาเหตุที่ผู้เข้าร่วมมักอ่อนแอ เรารู้อยู่แล้วเกี่ยวกับสิ่งนั้นและเพิ่งสงสัยว่าคุณจะประกาศการอ้างอิงโปรโตคอลที่อ่อนแอได้อย่างไร (ตอบได้อย่างสมบูรณ์โดย @flainez)
hnh

30
คุณถูก. ที่จริงฉันมีคำถามแบบเดียวกับคุณก่อนหน้านี้ แต่ฉันขาดข้อมูลพื้นฐานมากมาย ฉันได้อ่านข้างต้นและทำบันทึกเพิ่มเติมเพื่อช่วยให้ฉันเข้าใจปัญหาทั้งหมดที่เกี่ยวข้องกับคำถามของคุณ ตอนนี้ฉันคิดว่าฉันสามารถใช้คำตอบที่ได้รับการยอมรับของคุณและรู้ว่าทำไมฉันถึงทำมัน ฉันหวังว่ามันจะช่วยผู้ชมในอนาคตได้เช่นกัน
Suragch

5
แต่ฉันสามารถมีโพรโทคอลอ่อนที่ไม่ขึ้นอยู่กับชนิดหรือไม่ โปรโตคอลด้วยตนเองไม่สนใจว่าวัตถุจะสอดคล้องกับตัวเอง ดังนั้นทั้งชั้นเรียนหรือโครงสร้างสามารถสอดคล้องกับมัน เป็นไปได้หรือไม่ที่จะได้รับประโยชน์จากความสามารถในการปรับตัวให้เข้ากับสภาพแวดล้อม แต่มีประเภทคลาสที่ไม่สอดคล้องกันเท่านั้น?
FlowUI SimpleUITesting.com

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

36

AnyObject เป็นวิธีการอย่างเป็นทางการในการใช้การอ้างอิงที่อ่อนแอใน Swift

class MyClass {
    weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate: AnyObject {
}

จาก Apple:

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

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID276


7
น่าสนใจ ถูกclassคัดค้านในสวิฟท์ 4.1?
hnh

@hnh คุณยังสามารถสร้าง "pseudo-protocol" ได้ด้วยการสร้างคลาส แต่โพรโทคอล: AnyObject ทำสิ่งที่ OP ต้องการโดยมีผลข้างเคียงน้อยกว่าการสร้างคลาส (คุณยังไม่สามารถใช้โปรโตคอลที่มีประเภทค่าได้ แต่ประกาศว่าคลาสจะไม่แก้ปัญหานั้น)
Arru

8

อัปเดต: ดูเหมือนว่าคู่มือได้รับการอัปเดตแล้วและตัวอย่างที่ฉันอ้างถึงถูกลบไปแล้ว ดูการแก้ไขคำตอบของ @ flainez ด้านบน

Original: การใช้ @objc เป็นวิธีที่ถูกต้องแม้ว่าคุณจะไม่ได้ทำงานร่วมกับ Obj-C ก็ตาม มันทำให้แน่ใจว่าโปรโตคอลของคุณจะถูกนำไปใช้กับชั้นเรียนและไม่ enum หรือ struct ดู "การตรวจสอบความสอดคล้องของโปรโตคอล" ในคู่มือ


ดังที่กล่าวมานี้ IMO ไม่ใช่คำตอบสำหรับคำถาม โปรแกรม Swift ธรรมดาควรจะสามารถยืนได้ด้วยตัวเองโดยไม่มีการผูกติดกับ NS'ism (นี่อาจบอกเป็นนัยว่าไม่ได้ใช้ตัวแทนของอีกต่อไป แต่เป็นการออกแบบการสร้างอื่น ๆ ) Swift MyClass ที่บริสุทธิ์ของฉันจริง ๆ แล้วไม่สนใจว่าปลายทางเป็น struct หรือวัตถุหรือฉันไม่ต้องการตัวเลือก บางทีพวกเขาอาจจะแก้ไขในภายหลังมันเป็นภาษาใหม่หลังจากทั้งหมด อาจเป็นเช่น 'คลาสโพรโทคอล XYZ' หากต้องการความหมายของการอ้างอิง?
hnh

4
ฉันคิดว่ามันก็เป็นที่น่าสังเกตว่า \ @objc มีผลข้างเคียงเพิ่มเติม - ข้อเสนอแนะของ NSObjectProtocol ของ @eXERYed ดีกว่านิดหน่อย ด้วย \ @objc - ถ้าผู้ร่วมคลาสใช้อาร์กิวเมนต์ของวัตถุเช่น 'handleResult (r: MySwiftResultClass)' MySwiftResultClass จะต้องสืบทอดจาก NSObject! และน่าจะไม่ใช่เนมสเปซเช่นกันอีกต่อไป ฯลฯ โดยย่อ: \ @objc เป็นคุณสมบัติเชื่อมต่อไม่ใช่ภาษาเดียว
hnh

ฉันคิดว่าพวกเขาแก้ไขปัญหานี้แล้ว ตอนนี้คุณเขียน: โปรโตคอล MyClassDelegate: class {}
3675131

เอกสารเกี่ยวกับเรื่องนี้อยู่ที่ไหน ฉันตาบอดหรือทำสิ่งผิดปกติเพราะฉันไม่สามารถหาข้อมูลเกี่ยวกับสิ่งนี้ได้ ... O_O
BastiBen

ฉันไม่แน่ใจว่ามันตอบคำถามของ OP หรือไม่ แต่สิ่งนี้มีประโยชน์โดยเฉพาะอย่างยิ่งถ้าคุณกำลังทำงานร่วมกับ Objc-C;)
Dan Rosenstark

-1

โปรโตคอลจะต้องเป็น subClass ของ AnyObject, class

ตัวอย่างที่ระบุด้านล่าง

    protocol NameOfProtocol: class {
   // member of protocol
    }
   class ClassName: UIViewController {
      weak var delegate: NameOfProtocol? 
    }

-9

Apple ใช้ "NSObjectProtocol" แทน "class"

public protocol UIScrollViewDelegate : NSObjectProtocol {
   ...
}

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


5
ไม่เกี่ยวข้องกับคำถามคำถามนี้เกี่ยวกับการสร้างคลาส Swift ล้วนๆ (โดยเฉพาะไม่มี NSObject) ที่สนับสนุนวัตถุผู้ร่วมประชุม ไม่เกี่ยวกับการใช้โปรโตคอล Objective-C ซึ่งเป็นสิ่งที่คุณกำลังทำ หลังต้องใช้ @objc aka NSObjectProtocol
hnh

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