Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift delegate protocol cannot prevent retain cycle issue

In Swift, if I create a delegate protocol, it can be conformed to by class and struct.

protocol MyDelegate {
  // Can be conformed to by class or struct
}

The issue comes up when I declare the delegate. If the delegate is a class instance, I want the variable to be weak to avoid retain cycle. If it is a struct, there is no such need - in fact, Swift won't allow me to make the delegate variable weak. Note: I know how to create a weak delegate, but the key question is - if you create a delegate protocol that can be weak, unless you make it class-conforming only, you cannot enforce retain cycle.

class MyClass {
  // Want weak var here to avoid cyclical reference
  // but Swift won't allow it because MyDelegate can be
  // conformed by struct as well.  Dropping weak means
  // cyclical reference cannot be prevented
  weak var delegate: MyDelegate?
}

class MyConformingClass: MyDelegate {

}

or

struct MyConformingStruct: MyDelegate {

}

It seems like we need to declare the protocol to be for class only at all times like this because a non regular delegate protocol cannot prevent retain cycles:

protocol MyDelegate: class {

}

The fact that Swift allows you to shoot yourself in the foot this way seems to go against its design philosophy in safety.

like image 635
Boon Avatar asked Dec 28 '15 12:12

Boon


People also ask

How do I stop retaining cycle in Swift?

For example; when we set mercedes instance to nil , the compiler cannot decrease its ARC to 1 because a Car object is still referencing the mercedes and vice versa. In order to prevent this retain cycle, we need to declare at least one of the variable as weak or unowned.

Why delegates are weak in Swift?

Why delegate should be weak var? Before you begin I highly recommend you to check ARC story. We will design protocol and classes in order to show retain cycle on delegates. With using lazy keyword we are not initializing delegate which means there is no memory leak right now.

Which of the following can cause a retain cycle?

Structs and Classes A choice between a struct and a class can cause a retain cycle. Both structs and classes can have constants, variables, functions and protocols.

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.


Video Answer


2 Answers

If you really want to support a protocol on a class or struct you can always store the delegate in separate underlying variables. That way you can have one weak for when the delegate is a class. Along the lines of the following:

protocol MyDelegate {
  // Can be conformed to by class or struct
}

class MyClass {
    private weak var delegateFromClass: AnyObject?
    private var delegateFromStruct: MyDelegate?
    var delegate: MyDelegate? {
        get {
            return (delegateFromClass as? MyDelegate) ?? delegateFromStruct
        }
        set {
            if newValue is AnyObject {
                delegateFromClass = newValue as? AnyObject
                delegateFromStruct = nil
            } else {
                delegateFromClass = nil
                delegateFromStruct = newValue
            }
        }
    }
}

class MyConformingClass: MyDelegate {

}

struct MyConformingStruct: MyDelegate {

}

print(" \(MyConformingClass() is AnyObject) \(MyConformingStruct() is AnyObject)")
like image 126
Michael Avatar answered Nov 15 '22 06:11

Michael


It takes two to have a retain cycle...

If you really want to do it this way, then why not leave out the weak and just make it a strong reference. You would only have a problem if the delegate also maintained a strong reference to the object that it was delegating for.

So it becomes the duty of the delegate to make sure any reciprocal references are weak, which should be possible all of the time since MyClass is a class, and therefore you can always declare a weak reference to it.

like image 44
Aaron Rasmussen Avatar answered Nov 15 '22 07:11

Aaron Rasmussen