Swift delegate protocol cannot prevent retain cycle issue - swift

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.

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)")

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.

Related

Is there a way to check if a delegate is a value or a reference type?

Lets say we had something like this:
protocol Delegate {}
struct Value: Delegate {}
class Reference: Delegate {}
struct Test {
let delegate: Delegate
}
How could we know if a delegate is a struct (value type) or a class (reference type)?
First thought that comes to mind is to check memory address equality after making a copy of a delegate:
struct Test {
let delegate: Delegate
var isReferenceType: Bool {
let copy = delegate
let copyAddress = // ... get memory address of a copy
let originalAddress = // ... get memory address of an original
return copyAddress == originalAddress
}
}
Is it even possible to do this?
Is there more elegant/correct way of doing this?
Copying a value type might potentially be an expensive operation?
Every class conforms to the AnyClass protocol. However enums and structs won't. Utilising that you can check if it's a class or a struct(or even an enum)
struct Test {
let delegate: Delegate
var isReferenceType: Bool {
return type(of:delegate) is AnyClass
}
}

weak property conforming to protocol with generic constraint

I wrote simplified version of similar implementation I'm having problem with. Anyone knows why is that and eventually how to workaround?
NOTE: the code is just example to keep it as simple as possible
protocol Alertable {
associatedtype Alert
func show(alertOfType alertType: Alert)
}
protocol ViewControllerDelegate: class, Alertable {
}
final class MyViewController: UIViewController {
// MARK: - Types
enum AlertType {
case alert
case warning
}
}
extension MyViewController: ViewControllerDelegate {
typealias Alert = AlertType // ! here i specify the associated type !
func show(alertOfType alertType: Alert) {
// code..
}
}
So far so good. But, here I get errors:
final class ViewModel {
// ERROR: Protocol 'ViewControllerDelegate' can only be used as a generic constraint because it has Self or associated type requirements.
weak var viewController: ViewControllerDelegate?
init(viewController: ViewControllerDelegate?) {
self.viewController = viewController
}
private func someFunction() {
// ERROR: Member 'show' cannot be used on value of protocol type 'NewsFeedViewControllerInput'; use a generic constraint instead.
viewController?.show(alertOfType: .warning)
// code..
}
}
Thank you
You had a bit of a misunderstanding here. When you define:
protocol ViewControllerDelegate: class, Alertable {}
extension MyViewController: ViewControllerDelegate {
typealias Alert = AlertType // ! here i specify the associated type !
func show(alertOfType alertType: Alert) {
// code..
}
}
The typealias is defined in MyViewController but not ViewControllerDelegate. It's not clear why you need ViewControllerDelegate in this question but maybe there's something we don't see in the real app.
In ViewModel, change from ViewControllerDelegate to MyViewController:
final class ViewModel {
weak var viewController: MyViewController?
// ...
}
One more thing, though unrelated to the error: you use many final classes. Should they be structs instead?

Weak keyword with computed properties

private weak var _delegate: SomeClassDelegate?
weak var delegate: SomeClassDelegate? {
get {
return _delegate
}
set {
_delegate = newValue
}
}
This is valid code. Is there is any sense in using weak keyword with computed delegate property? Logically no; how compiler will process through this code?
Is there is any sense in using weak keyword with computed delegate property?
Not only is it sensible to do so, but it’s important to do so. This computed property is your public interface to this private hidden property. If the computed property lacks the weak qualifier, callers will draw incorrect conclusions about the underlying semantics.
Consider:
class SomeClass {
private weak var _delegate: SomeClassDelegate?
var delegate: SomeClassDelegate? { // whoops; this should be `weak`
get { return _delegate }
set { _delegate = newValue }
}
}
And
class CustomDelegate: SomeClassDelegate { ... }
Then
let object = SomeClass()
object.delegate = CustomDelegate()
In the absence of the the weak qualifier on the computed property and without diving into the implementation details, the programmer might incorrectly conclude that the above is fine. But it’s not. Because the underlying _delegate is weak, this CustomDelegate() instance will be deallocated immediately, and the object will end up with no delegate object. And there’s no compiler warning about this behavior.
If, however, we fix SomeClass like so:
class SomeClass {
private weak var _delegate: SomeClassDelegate?
weak var delegate: SomeClassDelegate? { // great; matches underlying semantics
get { return _delegate }
set { _delegate = newValue }
}
}
Then the programmer will receive a very helpful warning:
let object = SomeClass()
object.delegate = CustomDelegate() // results in "Instance will be immediately deallocated because property 'delegate' is 'weak'"
They’ll then correctly deduce that they should keep their own strong reference to this CustomDelegate for the code to work properly.
So, bottom line, you don’t technically need the weak qualifier on the computed property that is backed by a private weak stored property, but it’s prudent to do so to avoid mysterious bugs and/or misunderstandings about the underlying semantics.
Computed properties aren't retain by ARC, so you don't need to mark it as weak.
Only one pros that I know about is to ensure that property could be nil in future. You cannot declare it as:
weak var youProperty: YourType {
get {
return _yourProperty
}
set {
_yourProperty = newValue
}
}

Swift protocol nested in a class

I would like to nest a protocol in my class to implement the delegate pattern like so :
class MyViewController : UIViewController {
protocol Delegate {
func eventHappened()
}
var delegate:MyViewController.Delegate?
private func myFunc() {
delegate?.eventHappened()
}
}
But the compiler will not allow it :
Protocol 'Delegate' cannot be nested inside another declaration
I can easily make it work by declaring MyViewControllerDelegate outside of the class scope.
My question is why such a limitation ?
according to the swift documenation
Swift enables you to define nested types, whereby you nest supporting enumerations, classes, and structures within the definition of the type they support.
Given protocols are not on that list, it doesn't appear that it's currently supported.
It's possible they will add the feature at some point, (Swift was announced less than 2 years go after all). Any idea on why they won't or haven't would be speculation on my part.
this is my work around:
protocol MyViewControllerDelegate : class {
func eventHappened()
}
class MyViewController : UIViewController {
typealias Delegate = MyViewControllerDelegate
weak var delegate: Delegate?
private func myFunc() {
delegate?.eventHappened()
}
}
A separate problem with your class is that delegate does not have a concrete type. You can get away with declaring it a MyViewController.Delegate? because it is an optional type and can be .None. But that just makes your private myFunc dead code. Only enumerations, classes, and structures can conform to a protocol. Which of those three types is delegate?
That said, this is not the cause of your problem. When I make a real type for delegate and make it conform to the Delegate protocol, I still get the same error.
class MyViewController: UIViewController {
protocol Delegate {
func eventHappened()
}
class Classy: Delegate {
func eventHappened() {
print("eventHappened")
}
}
var delegate: Classy? = Classy()
func myFunc() {
delegate?.eventHappened()
}
}
As an esoteric exercise this might be interesting in pushing the bounds of what a class might do but no one should every try to declare a protocol inside of a class. Protocols are all about type composition and collections. There is no code reuse scenario when you are limited to being in the same outer class.

How to declare a swift class with a property that conforms to some protocol

Is it possible for swift to have a ViewController class, initialized from xib, has a property that is also a subclass of UIViewController and conforms to some protocol?
protocol SomeProtocol {
// Some methods
}
class ViewController: UIViewController {
// contentView is initialized from xib
#IBOutlet weak var contentView: UIView!
// I'd like to declare anotherViewController both conforms to 'SomeProtocol'
// and a subclass of UIViewController
var anotherViewController: UIViewController!
...
}
When I declare ViewController as an generic class, say class ViewController<T: UIViewController, SomeProtocol>, I get an error :
"Variable in a generic class cannot be presented in Objective-C"
So how can I fulfil it if I cannot use generic class?
Please forgive me if I misunderstood your problem, but I think what you want to do is declare a new type that inherits from UIViewController and conforms to SomeProtocol, like so:
protocol SomeProtocol { }
class VCWithSomeProtocol: UIViewController, SomeProtocol {
}
class ViewController: UIViewController {
var anotherViewController: VCWithSomeProtocol!
}
So I hope I am not misunderstanding the question as well, but it sounds like you may want a multiple-inheritance object level mixin such as:
let myVC: ViewController, SomeProtocol
Unfortunately, Swift does not support this. However, there is a somewhat awkward work-around that may serve your purposes.
struct VCWithSomeProtocol {
let protocol: SomeProtocol
let viewController: UIViewController
init<T: UIViewController>(vc: T) where T: SomeProtocol {
self.protocol = vc
self.viewController = vc
}
}
Then, anywhere you need to do anything that UIViewController has, you would access the .viewController aspect of the struct and anything you need the protocol aspect, you would reference the .protocol.
For Instance:
class SomeClass {
let mySpecialViewController: VCWithSomeProtocol
init<T: UIViewController>(injectedViewController: T) where T: SomeProtocol {
self.mySpecialViewController = VCWithSomeProtocol(vc: injectedViewController)
}
}
Now anytime you need mySpecialViewController to do anything UIViewController related, you just reference mySpecialViewController.viewController and whenever you need it to do some protocol function, you reference mySpecialViewController.protocol.
Hopefully Swift 4 will allow us to declare an object with protocols attached to it in the future. But for now, this works.
Hope this helps!