How can I make a weak protocol reference in 'pure' Swift (without @objc)

127,917

Solution 1

You need to declare the type of the protocol as AnyObject.

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

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}

Using AnyObject you say that only classes can conform to this protocol, whereas structs or enums can't.

Solution 2

Supplemental Answer

I was always confused about whether delegates should be weak or not. Recently I've learned more about delegates and when to use weak references, so let me add some supplemental points here for the sake of future viewers.

  • The purpose of using the weak keyword is to avoid strong reference cycles (retain cycles). Strong reference cycles happen when two class instances have strong references to each other. Their reference counts never go to zero so they never get deallocated.

  • You only need to use weak if the delegate is a class. Swift structs and enums are value types (their values are copied when a new instance is made), not reference types, so they don't make strong reference cycles.

  • weak references are always optional (otherwise you would used unowned) and always use var (not let) so that the optional can be set to nil when it is deallocated.

  • A parent class should naturally have a strong reference to its child classes and thus not use the weak keyword. When a child wants a reference to its parent, though, it should make it a weak reference by using the weak keyword.

  • weak should be used when you want a reference to a class that you don't own, not just for a child referencing its parent. When two non-hierarchical classes need to reference each other, choose one to be weak. The one you choose depends on the situation. See the answers to this question for more on this.

  • As a general rule, delegates should be marked as weak because most delegates are referencing classes that they do not own. This is definitely true when a child is using a delegate to communicate with a parent. Using a weak reference for the delegate is what the documentation recommends. (But see this, too.)

  • Protocols can be used for both reference types (classes) and value types (structs, enums). So in the likely case that you need to make a delegate weak, you have to make it an object-only protocol. The way to do that is to add AnyObject to the protocol's inheritance list. (In the past you did this using the class keyword, but AnyObject is preferred now.)

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

Further Study

Reading the following articles is what helped me to understand this much better. They also discuss related issues like the unowned keyword and the strong reference cycles that happen with closures.

Related

Solution 3

AnyObject is the official way to use a weak reference in Swift.

class MyClass {
    weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate: AnyObject {
}

From Apple:

To prevent strong reference cycles, delegates should be declared as weak references. For more information about weak references, see Strong Reference Cycles Between Class Instances. Marking the protocol as class-only will later allow you to declare that the delegate must use a weak reference. You mark a protocol as being class-only by inheriting from AnyObject, as discussed in Class-Only Protocols.

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

Solution 4

Update: It looks like the manual has been updated and the example I was referring to has been removed. See the edit to @flainez's answer above.

Original: Using @objc is the right way to do it even if you're not interoperating with Obj-C. It ensures that your protocol is being applied to a class and not an enum or struct. See "Checking for Protocol Conformance" in the manual.

Solution 5

The weak qualifier only applies to reference objects. Unless you add the @objc, AnyObject, or class qualifier on your protocol, the object conforming to the protocol might not be a reference object.

Thus you need on of those qualifiers (and AnyObject is recommended, since class is expected to be deprecated.)

By the way, note that adding @objc to your classes and properties is sometimes required, even in "pure Swift" applications. It doesn't have to do with you development language. It causes the compiler to build your code in a way that is compatible with the Objective-C runtime, which is required for some OS interfaces (target/action and old-style key paths for example)

Share:
127,917
hnh
Author by

hnh

I'm a professional software developer for ~25 years. Did code on all kinds of platforms (NeXTstep, Linux, Windows, macOS, iOS) in all kinds of languages (Objective-C, TCL, Guile, Python, Java, JavaScript, C++, Swift, ...). Running my own company: ZeeZide, been at Apple, ZideOne, Brainstorm and Skyrix before. Got an interesting project that fits my profile? Drop me a line! ZeeZide's primary product is Shrugs.app 🤷‍♀️ a native macOS client for Slack. Alongside I blog (mostly about Swift) at the Always Right Institute: SwiftWebUI, µTutorial on SwiftNIO, @dynamicCallable part1, part2, part3, Chat w/ your Raspi, Direct2SwiftUI and more. Tweeting at @helje5, homepage. Making FOSS since like forever. Early work I'm responsible for: ScalableOGo, OpenGroupware.org, SOPE and GTKKit (also worked on libFoundation, GCC libobjc and GNUstep, and Internet standards like CalDAV and CardDAV). Since it was released I've primarily worked on Swift projects. I tend to create GitHub organizations for larger projects: SwiftBlocksUI, Macro.swift, SwiftWebUI, Direct to Swift, SPMDestinations, mod_swift, ZeeQL, PL/Swift, Noze.IO, SwiftObjects, SwiftyLinkerKit, … My own favorites: Macro.swift (Node.js style Server Side Swift), SwiftNIO IRC Server (chat server, w/ Web frontend, SwiftUI client and Eliza), Direct to Swift (rule driven SwiftUI database apps) and UXKit (UIKit and AppKit apps from a single source).

Updated on June 18, 2021

Comments

  • hnh
    hnh about 3 years

    weak references don't seem to work in Swift unless a protocol is declared as @objc, which I don't want in a pure Swift app.

    This code gives a compile error (weak cannot be applied to non-class type MyClassDelegate):

    class MyClass {
      weak var delegate: MyClassDelegate?
    }
    
    protocol MyClassDelegate {
    }
    

    I need to prefix the protocol with @objc, then it works.

    Question: What is the 'pure' Swift way to accomplish a weak delegate?

  • hnh
    hnh about 10 years
    As mentioned this is IMO not an answer to the question. A plain Swift program should be able to stand on it's own w/o being tied to NS'ism (this might imply not using delegate's anymore but some other design construct). My pure Swift MyClass actually doesn't care whether the destination is a struct or object, nor do I need optionals. Maybe they get to fix it later, it's a new language after all. Possibly something like 'class protocol XYZ' if reference semantics are required?
  • hnh
    hnh about 10 years
    I think it's also worth noting that \@objc has additional side effects - the NSObjectProtocol suggestion of @eXhausted is a little better. With \@objc - if the class delegate takes an object argument, like 'handleResult(r: MySwiftResultClass)', the MySwiftResultClass now needs to inherit from NSObject! And presumably it's not namespace'd anymore either, etc. In short: \@objc is a bridging feature, not a language one.
  • Jim T
    Jim T almost 10 years
    My problem with this solutions is that calling the delegate causes a crash - EXC_BAD_ACCESS (as noted by others elsewhere). This seems to be bug. The only solution I have found is to use @objc and eliminate all Swift data types from the protocol.
  • user3675131
    user3675131 almost 10 years
    I think they have resolved this. You now write: protocol MyClassDelegate : class { }
  • BastiBen
    BastiBen over 9 years
    Where is the documentation on this? Either I'm blind or doing something wrong, because I can't find any information about this... O_O
  • C0D3
    C0D3 over 9 years
    What's the correct way of doing weak delegates now in Swift? Apple documentation is not showing or declaring the delegate as weak in their example code: developer.apple.com/library/ios/documentation/swift/conceptu‌​al/…
  • hnh
    hnh over 8 years
    This is all nice and interesting, but is not really related to my original question - which is neither about weak/ARC itself nor about why delegates are usually weak. We already know about all that and just wondered how you can declare a weak protocol reference (answered perfectly well by @flainez).
  • Suragch
    Suragch over 8 years
    You're right. I actually had the same question as you earlier, but I was missing a lot of this background information. I did the above reading and made the supplemental notes to help myself understand all the issues related to your question. Now I think I can apply your accepted answer and know why I'm doing it. I hope maybe it will help future viewers as well.
  • hnh
    hnh about 8 years
    Not relevant for the question, this question is about building a pure Swift class (specifically no NSObject) supporting a delegate object. It is not about implementing Objective-C protocols, which is what you are doing. The latter requires @objc aka NSObjectProtocol.
  • Dan Rosenstark
    Dan Rosenstark about 8 years
    I'm not sure if it answers the OP's question or not, but this is helpful especially if you are interoperating with Objc-C ;)
  • Just a coder
    Just a coder almost 8 years
    But can i have a weak protocol that does NOT depend on the type? A protocol by its self does not care what object is conforming to itself. So both a class, or a struct can conform to it. Is it possible to still have the benefit of both being able to conform to it, but only have the class types that conform be weak?
  • DawnSong
    DawnSong almost 8 years
    OK, but not be recommended.
  • Suragch
    Suragch over 7 years
    @JimT, does this still give you a crash? You comment had a lot of upvotes but I am wondering if this was just in old versions of Swift. I've never gotten a crash with Swift 2 or 3.
  • adamF
    adamF over 7 years
    re: "only on classes and no other stuff like enums or structs" anyone know what the proper term for "stuff" is? types?
  • Andrew Johnson
    Andrew Johnson almost 7 years
    @adamF maybe value types?
  • hnh
    hnh over 6 years
    Interesting. Is class deprecated in Swift 4.1?
  • Trev14
    Trev14 almost 6 years
    This is not always safe - remember that you only need to make the delegate weak if it also holds a reference to the delegator & you need to break that strong reference cycle. If the delegate holds no reference to the delegator, the delegate can go out of scope (because it's weak) and you'll have crashes and other problems :/ something to keep in mind.
  • Arru
    Arru over 5 years
    @hnh You can still make a "pseudo-protocol" by making it a class, but protocol: AnyObject does exactly what the OP is asking for with less side effects than making it a class. (you still can't use such a protocol with value types, but declaring it a class won't solve that either)
  • hnh
    hnh about 5 years
    BTW: I think the "new style" (Swift 5) is to do protocol ProtocolNameDelegate: AnyObject, but doesn't matter.
  • José
    José about 5 years
    It should be AnyObject since class will be deprecated at some point.
  • Victor Jalencas
    Victor Jalencas over 4 years
    > because most delegates are referencing classes that they do not own I would rewrite this as: most delegators. Otherwise the non owned object becomes the owner
  • Mykhailo Lysenko
    Mykhailo Lysenko about 3 years
    @C0D3 actually it does in the link you've proviced. Maybe because currently it is 2021... but just search the line weak var delegate: DiceGameDelegate?