Why use delegate and protocol instead of just passing an instance in Swift?
Asked Answered
F

5

18

I was trying to pass around variables between views in Swift, and ran into the rather abstract concept of protocols and delegates.

Then I tried storing a reference to the first view in a second view and call functions on that directly. This seems to work:

SCREEN 1

class Screen1: UIViewController {

    var myName = "Screen1"

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    //
    // checking if the segue to screen 2 is called and then passing a reference
    //
    override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
        if segue.identifier == "screen2Segue"{
            let vc = segue.destinationViewController as Screen2
            vc.storedReference = self
        }
    }

    func getName() -> String {
        return myName
    }
}

SCREEN 2

class Screen2: UIViewController {

    var storedReference:Screen1!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    func testReference() {
        // calling a function on the stored reference to screen 1
        var str = storedReference.getName()
        println("Leaving screen 2, going to " + str)
    }
}

My question: what's wrong with this code? Why use delegates and protocols if you can just pass around a reference directly?

Perhaps related: when does a view get un-initialized and replaced by an entirely new view instance? Am I calling 'getName()' on an old instance?

Fulgurant answered 25/7, 2014 at 16:37 Comment(2)
I don't see why this question is voted primarily opinion based. Protocols and the delegate pattern exist for good reason and the way they work is not matter of opinion.Curative
I was wondering about private and internal methods and properties. If those also exist to protect the inner workings of an object from outside influence, wouldn't that be a valid approach in certain cases? The above examples mention code (tableview) that is specifically built in Apple's API to allow third party developers to use table views. But in my case, I'm just building a simple private two-page app, that will never be used or accessed by anyone including myself.Fulgurant
S
14

Protocols are useful for separating implementation from interface, which helps increase code reusability, understandability, and testability.

For example, perhaps you wish to store items in a List of some sort. Some possible implementations of a List include array-based implementations and node-based (linked-list) implementations. If you were to declare a protocol called List and have classes ArrayList and LinkedList that implemented that protocol, anything that required the use of a list (variable passed as a parameter to a method, a property, etc) could use List as the variable type, and be able to function without caring about whether a the list was an ArrayList or a LinkedList. You could change which type was used, or how they were implemented, and it would not matter to whatever was using them, because only the exposed interface declared in the protocol would be visible.

Protocols can also be useful for emulating something like multiple inheritance, as a class can inherit from a superclass, as well as implement one or more interfaces. (eg. A bat is both a mammal and winged, so it could be represented as a Bat class inheriting from a Mammal class that implements the Winged protocol).

The delegate pattern uses protocols to delegate some responsibilities to another object, which is especially good for code separation and reusability. For example, the UITableViewDelegate protocol in iOS allows a UITableView to react to things like cell selection by delegating another object to handle the event. This has probably been used by millions of objects in thousands of applications, without the developers at Apple who implemented UITableView and UITableViewDelegate having ever known anything about the objects that were implementing the protocol.

By directly passing a reference between your view controllers, you are forcing the second to be completely dependent upon the first. If you ever wished to change the flow of your application so that the second view controller could be accessed from somewhere else, you would be forced to rewrite that view controller to use the new origin. If you use a protocol instead, no changes to the second view controller would have to be made.

Speciality answered 25/7, 2014 at 17:45 Comment(5)
What if you have only 1 view controller? I'm writing an app extension, it has only 1 view controllers and it has some child uiviews. It really hard to use protocol there. Example: in the root view, i have a child views, and this one has custom gesture, typer, a class to manipulate of changing views if user interacted. By using protocol, i have to pass delegate through multiple levels - from root view to child views , root view to custom gestures , typer and some classes ... I've tried to use global variable and i think its a better design pattern?Kerby
@Kerby The thing about design patterns is that they are generally intended to keep your code more maintainable, but typically at the cost of being more complex in the short term. This means that you need to pick patterns appropriate for your application. For a simple project like yours that has only a single view controller and that presumably only you are working on, I agree that a global variable is probably better for your use case. If instead you had hundreds of classes and 10 other people working on it, you definitely wouldn't want a global variable floating around.Speciality
What i care the most is about memory usage. Do you know what's diferent between passing instance , using protocol and global variables?Kerby
@Kerby as references are used in both variants, there is no difference with regard to memory usage.Inaccuracy
@Inaccuracy Wrong. I've tested using global variables, and these variables will be cached. If you use app extension, you can recall those variables instead of re-declare itKerby
O
9

It is a basic design principle to not expose any more of a design than you have to. By passing the reference around you are exposing the whole object. Which means that others can call any of its functions and access any of its properties. And change them. This isn't good. Besides letting others use the object in ways it might not have intended, you will also run into issues if you try to change the object in the future and find out that it breaks somebody else who was using something you didn't intend. So, always a good idea to not expose anything that you don't have to. This is the purpose of delegates and protocols. It gives the object complete control over what is exposed. Much safer. Better design.

Overleap answered 25/7, 2014 at 17:10 Comment(2)
But if you can make methods and properties private or internal, aren't you also protecting them from access?Fulgurant
Yes you are. Protocols and delegates extend that. If your class were to provide three types of services, each only intended to be used by one other type, you could declare a protocol for each of them. And other classes could declare themselves to be a delegate for just the appropriate one they want to use. And because you only expose an API you are free to make internal changes to your class without the possibility of breaking others using your class.Overleap
C
5

I think you didn't fully get the understanding what protocols are.

I always say protocols are like contracts.
The delegate object that implements a certain protocols promises that it can do things the delegator can't do.

In real world I have a problem with my house's tubes.
I (the delegator) call a plumber (the delegate) to fix it. The plumber promises (by contract) to be able to duo it. The promise is the protocol. I don't care how he do it, as long as he does it.

But these contracts are not only useful for delegation.
I am just writing a food ordering app. As it has a menu it need item to display in it.
I could go with basic inheritance and write a class MenuItem, that all sub classes must inherit from.
Or I write an protocol to express: «No matter what object you are, as long as you fulfill this contract we have a deal». this allows me to create many different classes or annotate existing classes in categories, although I don't have the tool of multiple inheritance.

Actually I do both: I write a protocol MenuItem and a class MenuItem that conforms to the protocol. Now I can use simple inheritance or use classes that do not inherit from the class MenuItem.

Code in Objective-C (sorry: I am still transitioning to Swift)

@protocol MenuItem <NSObject>

-(NSString *)name;
-(double) price;
-(UIColor *)itemColor;

@end


@interface MenuItem : NSObject <MenuItem>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) double price;
@property (nonatomic, strong) UIColor *itemColor;

@end

#import "MenuItem.h"

@implementation MenuItem

-(id)initWithCoder:(NSCoder *)decoder
{
    self = [super init];
    if (self) {
        self.name = [decoder decodeObjectForKey:@"name"];
        self.price = [decoder decodeDoubleForKey:@"price"];
        self.itemColor = [decoder decodeObjectForKey:@"itemColor"];
    }
    return self;
}

-(void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeDouble:self.price forKey:@"price"];
    [encoder encodeObject:self.name forKey:@"name"];
    [encoder encodeObject:self.itemColor forKey:@"itemColor"];
}


@end

Apple uses the same Architecture for NSObject: there is a protocol and a class NSObject. This allows classes, that aren't intact inheriting from the class NSObject to act ash an NSObject. One famous example:NSProxy.


in your case Screen1 promises to be able to understand messages that are send by the detail view controller Screen2. These allows decoupling: any object that does understand Screen1's protocol can be used. Also it helps to maintain a sane object tree, as we don't have to have circular imports. But in general you have to keep in mind that the delegator (Screen2) must keep a weak reference to it's delegate, otherwise we have a retain circle.


Of course an important example it UITableView:
The table view object knows everything about rendering it's cells, handling scrolling and so one. But the engineer who wrote it couldn't now how you want your table view look like. That's why he introduced a delegate to give you the chance to create the right cell. As he couldn't also know what your data looks like, he also introduced the datasource - that works exactly like a delegate: you will be asked to provide all information about your data, that are needed.

Curative answered 25/7, 2014 at 17:42 Comment(0)
S
2

This is mostly a matter of opinion so this question should probably be closed, but I think the developer community as a whole is in an agreement on this so I am going to answer it anyway.

An important concept in Software Architecture (the design of the structure of code) is called Separation of Concerns. The basic principle is that you should break down what your code has to do into small components that only have one distinct purpose. Each of these components should be able to stand mostly on their own without much concern with other components other than the ones it needs to directly be interacting with.

This helps greatly with code reuse. If you design a small component that is independent of most / if not all other components, you can easily plug that into other parts of your code or other applications. Take UITableView for example. By using the delegate pattern, every developer can easily create a table view and populate it with whatever data they want. Since that data source is a separate object (with the separate concern of coming up with the data) you can attach that same data source to multiple table views. Think of a contact list on iOS. You will want to access that same data in many ways. Instead of always rewriting a table view that loads the specific data and displays it in a specific way, you can reuse the data source with a different table view as many times as you want.

This also helps with the understandability of your code. It is tough for developers to keep too many thing in their head about the state of your app. If each of your code components are broken down into small, well defined responsibilities, a developer can understand each component separately. They can also look at a component, and make accurate assumptions about what it does without having to look at the specific implementation. This isn't such a big deal with small apps, but as code bases grow, this becomes very important.

By passing in a reference to your first view controller, you are making your second view controller completely dependent on the first. You cannot reuse the second view controller in another instance and its job becomes less clear.

There are lots of other benefits to separation of concerns but I believe those are two compelling and important ones.

Seductive answered 25/7, 2014 at 17:15 Comment(1)
What if it doesn't need to reuse the second view controller in another instance? You're talking in general about software architecture, i don't think we should bring UITableView in here for an example, in some particular small projects like extension apps, or some 1 ` UIViewController` app, if you apply this technique, it makes your code process slower and take more time to deploy and develop.Kerby
C
0

I think the problem with the latter arises with multiple reuse of a single class.

Take for example a custom UITableViewCell called CustomTableViewCell. Let's say you have Class A and Class B which both have tableViews and both would want to use CustomTableViewCell as their cell. You now have two options. Would you rather:

A. Use a delegate/protocol for CustomTableViewCell called CustomTableViewCellDelegate. Declare a single object inside the class CustomTableViewCell named "delegate" which implements the mentioned protocol and call on that regardless of what class it calls on

or

B. Declare an object for each class (Class A, Class B) inside CustomTableViewCell so you can hold a reference to each of them.

If you need to use CustomTableViewCell for a number of classes, then I think you know which option to take. Declaring multiple objects for different classes inside CustomTableViewCell would be a pain to see from a software architecture standpoint.

Covering answered 22/4, 2020 at 6:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.