Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift delegation - when to use weak pointer on delegate

Can someone explain when and when not to use a 'weak' assignment to a delegate pointer in Swift, and why?

My understanding is that if you use a protocol that is not defined as a class you cannot, nor want to, assign your delegate pointer to weak.

protocol MyStructProtocol{     //whatever }  struct MyStruct {     var delegate: MyStructProtocol? } 

However, when your protocol is defined as a class type protocol then you DO want to set your delegate to a weak pointer?

protocol MyClassProtocol: class{     //whatever }  class MyClass {     weak var delegate: MyClassProtocol? } 

Am I correct? In Apple's swift guide there class protocol examples aren't using weak assignments, but in my testing I'm seeing strong reference cycles if my delegates aren't weakly referenced.

like image 947
nwales Avatar asked May 05 '15 14:05

nwales


People also ask

Why We Use weak for delegate in Swift?

However, using a strong delegate property in a , b will never get deallocated since a is holding on to b strongly. Using a weak reference, as soon as b loses the strong reference from c , b will dealloc when c deallocs. Usually this is the intended behaviour, which is why you would want to use a weak property.

Why is delegate weak in IOS?

When you define a delegate object as property, it's used a weak reference in the object it is defined in(lets say A, i.e. the delegate object is a property of A). The delegate is assigned when you init A(let's say in B), then most likely you would assign A.

What is difference between protocol and delegate in Swift?

We can say Protocol as a set of rules. That rules can be optional or required like we have to use in protocol. Delegates is a message passing technique in objective C and swift. An object has to take care of that message.


2 Answers

You generally make class protocols weak to avoid the risk of a “strong reference cycle” (formerly known as a “retain cycle”). (Note, we now do that by adding the AnyObject protocol to a protocol’s inheritance list; see Class-Only Protocols; we do not use the class keyword anymore.) Failure to make the delegate weak does not mean that you inherently have a strong reference cycle, but merely that you could have one.

With struct types, though, the strong reference cycle risk is greatly diminished because struct types are not “reference” types, so it is harder to create strong reference cycle. But if the delegate object is a class object, then you might want to make the protocol a class protocol and make it weak.

In my opinion, making class delegates weak is only partially to alleviate the risk of a strong reference cycle. It also is a question of ownership. Most delegate protocols are situations where the object in question has no business claiming ownership over the delegate, but merely where the object in question is providing the ability to inform the delegate of something (or request something of it). E.g., if you want a view controller to have some text field delegate methods, the text field has no right to make a claim of ownership over the view controller.

like image 64
Rob Avatar answered Sep 25 '22 13:09

Rob


As Rob said:

It's really a question of "ownership"

That's very true. 'Strong reference cycle' is all about getting that ownership right.

In the following example, we're not using weak var. Yet both objects will deallocate. Why?

protocol UserViewDelegate: class {     func userDidTap() }  class Container {     let userView = UserView()     let delegate = Delegate()     init() {         userView.delegate = delegate     }      deinit {         print("container deallocated")     } }  class UserView {     var delegate: UserViewDelegate?      func mockDelegatecall() {         delegate?.userDidTap()     }      deinit {         print("UserView deallocated")     } }  class Delegate: UserViewDelegate {     func userDidTap() {         print("userDidTap Delegate callback in separate delegate object")     } } 

Usage:

var container: Container? = Container() container?.userView.mockDelegatecall() container = nil // will deallocate both objects 

Memory ownership graph (doesn't have cycle)

    +---------+container +--------+     |                             |     |                             |     |                             |     |                             |     |                             |     |                             |     v                             v userView +------------------> delegate 

In order to create a strong reference cycle, the cycle needs be complete. delegate needs to point back to container but it doesn't. So this isn't an issue. But purely for ownership reasons and as Rob has said here:

In an object hierarchy, a child object should not maintain strong references to the parent object. That is a red flag, indicating a strong reference cycle

So regardless of leaking, still use weak for your delegate objects.


In the following example, we're not using weak var. As a result neither of the classes will deallocate.

protocol UserViewDelegate: class {     func userDidTap() }  class Container: UserViewDelegate {     let userView = UserView()      init() {         userView.delegate = self     }      func userDidTap() {         print("userDidTap Delegate callback by Container itself")     }     deinit {         print("container deallocated")     } }  class UserView {     var delegate: UserViewDelegate?      func mockDelegatecall() {         delegate?.userDidTap()     }      deinit {         print("UserView deallocated")     } } 

Usage:

var container: Container? = Container() container?.userView.mockDelegatecall() container = nil // will NOT deallocate either objects 

Memory ownership graph (has cycle)

     +--------------------------------------------------+      |                                                  |      |                                                  |      +                                                  v  container                                           userview      ^                                                  |      |                                                  |      |                                                  |      +------+userView.delegate = self //container+------+ 

using weak var will avoid the strong reference cycle

like image 39
mfaani Avatar answered Sep 24 '22 13:09

mfaani