How can I make a weak protocol reference in 'pure' Swift (without @objc)
SwiftDelegatesSwift ProtocolsSwift Problem Overview
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
?
Swift Solutions
Solution 1 - Swift
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 - Swift
#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 usedunowned
) and always usevar
(notlet
) so that the optional can be set tonil
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 theweak
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 theclass
keyword, butAnyObject
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.
- Delegate documentation
- Swift documentation: Automatic Reference Counting
- "Weak, Strong, Unowned, Oh My!" - A Guide to References in Swift
- Strong, Weak, and Unowned – Sorting out ARC and Swift
#Related
Solution 3 - Swift
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.
Solution 4 - Swift
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 - Swift
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)
Solution 6 - Swift
protocol must be subClass of AnyObject, class
example given below
protocol NameOfProtocol: class {
// member of protocol
}
class ClassName: UIViewController {
weak var delegate: NameOfProtocol?
}
Solution 7 - Swift
Apple uses "NSObjectProtocol" instead of "class".
public protocol UIScrollViewDelegate : NSObjectProtocol {
...
}
This also works for me and removed the errors I was seeing when trying to implement my own delegate pattern.