# Protocols and Delegation in iOS

Language: en
Canonical: [Protocols and Delegation in iOS](<https://allluckly.cn/en/journal/2016-09-01-protocols-and-delegation-in-ios/>)
中文：[中文 HTML](<https://allluckly.cn/%E6%8A%95%E7%A8%BF/tougao57>)
English: [English HTML](<https://allluckly.cn/en/journal/2016-09-01-protocols-and-delegation-in-ios/>)
Published: 2016-09-01T00:00:00+08:00
Modified: 2026-10-23T10:52:08+08:00
Author: Sindri

Uses UITableView and custom controls to distinguish protocols from delegation. It compares delegate and block callbacks, then discusses how data sources and protocol abstractions can reduce coupling between objects.

Originally credited to Sindri; edited and archived by Bison.

[Source listed in the original](<http://sindrilin.com/ios-dev/2016/08/25/%E8%81%8A%E8%81%8A%E5%8D%8F%E8%AE%AE>)

English translation of the [Chinese original](<https://allluckly.cn/%E6%8A%95%E7%A8%BF/tougao57>), translated on 2026-10-23. The article body uses automated translation; app descriptions, historical reading notes and video subtitles receive separate editorial checks. Original publication dates, attribution, code and version notes are retained.

**Before you read** This technical note dates from 2015–2017. The examples have not been rerun as part of this archive update. Read API, dependency and tool details in the context of the original publication date.

What is a protocol? In Objective-C, declare one with `@protocol` and add properties and methods for conforming types to implement. From one perspective, it differs from the `category` mechanism while acting as a kind of `category`. Protocols are everywhere in daily development. The central example, `UITableView`, uses them to obtain data and handle events. Here is a very simple protocol:

Editor: [Bison](<https://allluckly.cn/en/>)  
 Source: [Sindri’s Notes](<http://sindrilin.com/ios-dev/2016/08/25/%E8%81%8A%E8%81%8A%E5%8D%8F%E8%AE%AE>)

```
@protocol CustomProtocol

- (void)doSomething;

@end
```

Many developers still equate `委托-代理` with `协议`. The former relies on the latter, but the latter can achieve abstraction and decoupling without the former. Before discussing what protocols can do, we need to understand what they are:

> A protocol specifies behaviour that conforming classes must implement.

## Protocol applications

#### *Delegate callbacks*

Almost everyone writes `UITableView` delegate and data-source methods. Apple's view is elegant and powerful. Even as its verbose data-source methods attract criticism, it is hard to imagine a better implementation of `UITableView`. This control demonstrates the power of `委托-代理`.

![1](<https://allluckly.cn/images/blog/tuogao/tougao57/1.jpg>)

The simplest and most direct protocol application is the `委托-代理` design pattern. When encapsulating custom controls, I like custom protocols for handling taps and other actions. To understand `代理`, I believe you should at least have defined your own delegate protocol:

```
@protocol SegmentControlDelegate

@optional
- (void)segmentControl: (SegmentControl *)segmentControl didSelectItemAtIndex: (NSUInteger)index;

@end


@interface SegmentControl: UIView

@property (nonatomic, weak) id<SegmentControlDelegate> delegate;

@end


@implementation SegmentControl

- (voice)clickItem: (UIButton *)item
{
    if ([_delegate respondsToSelector: @selector(segmentControl:didSelectItemAtInex:)]) {
        [_delegate segmentControl: self didSelectItemAtIndex: item.tag];
    }
}

@end
```

The code above sketches a delegate implementation for a segmented controller I once built. In project development, advantages of delegate callbacks include `接口目的性强、易于追溯调试`, among others.

#### *When to use delegates*

Alongside `Delegate`, another equally useful and popular mechanism is `Block`. For example, sharing features commonly return results through a `block`:

```
typedef NS_ENUM(NSInteger, ShareResult) {
    ShareResultSuccess,
    ShareResultFailed,
    ShareResultCancel
};

- (void)share {
    [shareManager shareWithResult: ^(ShareResult result) {
        switch (result) {
            case ShareResultSuccess:
                //....

            case ShareResultFailed:
                //....

            case ShareResultCancel:
                //....
        }
    }];
}
```

This code looks compact and concise. Returning the result through a `Delegate` feels quite different:

```
- (void)share
{
    ShareManager * shareManager = [[ShareManager alloc] initWithUrl: @"http://sindrilin.com" title: @"" content: @""];
    shareManager.delegate = self;
    [shareManager share];
}

- (void)shareManager: (ShareManager *)shareManager didCompleteShare: (ShareResult)result
{
    switch (result) {
            case ShareResultSuccess:
                //....

            case ShareResultFailed:
                //....

            case ShareResultCancel:
                //....
        }
}
```

The same logic in a `Delegate` does not look as `nice`. Why? My reasons are:

- A delegate lacks `Block`'s ability to capture context, so caller parameters must be passed in. Yet `ShareManager` serves no purpose inside the method.
- Compared with a `block`, a delegate splits sharing and result handling between two places, making the logic less compact.

Similarly, if the sharing example becomes a network request that also reports download progress, using only a `Block` might look like this:

```
- (void)requestData {
    [manager requestWithProgress: ^(CGFloat progress) {
        NSLog(@"download progress: %g", progress);
        self.progressView.progress = progress;
    } complete: ^(id receiveData, NSError * error) {
        if (error) {
            [self showErrorWithMsg: error.description];
        } else {
            // handle receive data
        }
    }];
}
```

The `Block` is now rather cluttered and hard to follow. This comparison suggests that a `Block` works better than a delegate when a task needs an additional operation at completion. For an event with multiple states requiring callbacks as each occurs or changes, defining a protocol is a better choice.

#### *Data sources*

Although Apple separates data sources and delegates, both belong to the `委托-代理` mechanism. Apple has not explained exactly how to divide them; from their names, the distinction is roughly:

- Data sources expose an interface supplying display data to controls.
- Delegates expose an interface for responding to control events.

For a custom control with a data source, when should that source be called? Following `UITableView` behavior, its data-source methods are not called in the following situations:

- UITableView has not been added to a view.
- One of UITableView’s dimensions is 0.
- UITableView has no delegate set.

First, address the first two issues. `UIView`, the superclass of all views, exposes `didMoveToSuperview`, called after the view is added with `addSubview:`. Override it and fetch data from the data source:

```
@protocol CustomViewDataSource<NSObject>

- (CustomViewCell *)customView: (CustomView *)customView cellForRowAtIndexPath: (IndexPath *)indexPath;    

@end


@interface CustomView

@property (nonatomic, weak) id<CustomViewDataSource> dataSource;

@end


@implementation CustomView

- (void)didMoveToSuperview
{
    [super didMoveToSuperview];
    if (!_delegate) { return; }
    if ( CGRectGetWidth(self.frame) == 0 || CGRectGetHeight(self.frame) == 0 ) { return; }

    for (IndexPath * index in _visableIndexPaths) {
        id cell = [_dataSource customView: self cellForRowAtIndexPath: index];
        // configure cell
        [self addSubview: cell];
    }
}

@end
```

Similar to ` didMoveToSuperview  `, `didMoveToWindow` is called once on either side of the former method. Appropriate overrides can defer some data-source calls, reducing the chance that many simultaneous calls lower the frame rate.

Although both use `委托-代理`, a data source requires more considerations than a simple delegate. Data-source design is therefore a further step in applying protocols.

#### *Protocol abstraction*

Here we will discuss `面向对象编程`, nearly any experienced developer can immediately name `面向对象编程` features: `多态、抽象、封装` greatly improve productivity, but their convenience has a cost. In Casa’s [Think beyond object-oriented habits](<http://casatwy.com/tiao-chu-mian-xiang-dui-xiang-si-xiang-yi-ji-cheng.html>)series discusses inheritance’s weaknesses.

> But how does this relate to the article?

iPad development has a special component, `UISplitViewController`, that divides the screen into smaller and larger regions and displays two controllers simultaneously. The left is a management view and the right shows details. With multiple possible detail views, a tap on the left can require extensive controller creation and configuration code:

```
#import "AppDelegate.h"
#import "AddContactViewController.h"
#import "ContactDetailViewController.h"

@interface MasterViewController ()

@property (nonatomic, weak) UISplitViewController * splitViewController;
@property (nonatomic, strong) NSArray * contacts;

@end

@implementation MasterViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    AppDelegate * appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    self.splitViewController = appDelegate.splitViewController;
}

- (void)contactDetail: (NSInteger)index
{
    ContactDetailViewController * contactDetailVC = [[ContactDetailViewController alloc] init];
    contactDetailVC.contact = _contacts[index];
    [_splitViewController showDetailViewController: contactDetailVC sender: self];
}

- (void)addContact
{
    AddContactViewController * addContactVC = [[AddContactViewController alloc] initWithNibName: @"AddContactViewController" bundle: nil];
    [_splitViewController showDetailViewController: addContactVC sender: self];
}

@end
```

In practice, the right side required more than a dozen screens, even before counting complex `nib` controllers. This made the left-side `MasterViewController` messy. Although only a weak reference was involved, `MasterViewController` still needed its containing `splitViewController`, and much controller creation and configuration code was repetitive. Protocols can improve this. Define a class construction method to unify creation of right-side controllers, plus a method for configuring their data:

```
@protocol DetailViewControllerProtocol

+ (instancetype)detailViewController;

- (void)configurateWithData: (id)data;

@end
```

Also define a delegate callback for the left controller, passing the class name of the right controller to show and its configuration data, so `splitViewController` can handle presentation itself:

```
///  MasterViewController.h
@protocol MasterViewControllerDelegate

- (void)showDetailWithClass: (Class)aClass data: (id)data;

@end


@interface MasterViewController: NSObject

@property (nonatomic, weak) id<MasterViewControllerDelegate> delegate;

@end



///  MasterViewController.m
@implementation MasterViewController- (void)contactDetail: (NSInteger)index

- (void)contactDetail: (NSInteger)index
{
    [self showDetail: NSClassFromString(@"ContactDetailViewController") data: _contacts[index]];
}

- (void)addContact
{
    [self showDetail: NSClassFromString(@"AddContactViewController") data: nil];
}


- (void)showDetail: (Class)aClass data: (id)data
{
    if ([_delegate respondsToSelector: @selector(showDetailWithClass:data:)]) {
        [_delegate showDetailWithClass: aClass data: data];
    }
}

@end
```

After the change, `MasterViewController` retains only the data and the right controller's name. Everything else is handled by its delegate, `UISplitViewController`:

```
#import "MasterViewController.h"
#import "DetailViewControllerProtocol.h"

@interface SplitViewController ()<MasterViewControllerDelegate>

@end

@implementation SplitViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    for (UIViewController * viewController in self.viewControllers) {
        if ([viewController isKindOfClass: [MasterViewController class]]) {
            MasterViewController * masterVC = (MasterViewController *)viewController;
            masterVC.delegate = self;
            break;
        }
    }
}

- (void)showDetailWithClass: (Class)aClass data: (id)data
{
    UIViewController<DetailViewControllerProtocol> * detailVC = [aClass detailViewController];
    [detailVC configurateWithData: data];
    [self showDetailViewController: detailVC sender: self];
}

@end
```

Finally, each right-hand controller implements the protocol to create and configure itself:

```
@implementation  ContactDetailViewController

+ (instancetype)detailViewController
{
    return [[[self class] alloc] init];
}

- (void)configurateWithData: (Contact *)data
{
    self.phone = data.phone;
    self.fullName = data.fullName;
}

@end

@implementation AddContactViewController

+ (instancetype)detailViewController
{
    return [[[self class] alloc] initWithNibName: NSStringFromClass([self class) bundle: nil];
}

- (void)configurateWithData: (id)data {  }

///  More. Load from storyboard etc

@end
```

This example abstracts common controller behavior—creation and configuration—into a protocol to reduce repeated code. It also lowers coupling: any controller implementing the protocol can be presented in this `splitViewController`.

## Closing remarks

When first teaching myself iOS, I could not understand the point of protocols. After discovering `Block`, I thought it was so useful that delegates seemed pointless. I overused `Block` until realizing it was not magic and could introduce subtle bugs, then returned to `Delegate`. Since then, I have experimented with custom controls using notifications, delegates, and `Block` callbacks. This article is, strictly speaking, a record of one stage of that learning.

---

Recommended reading: [Implement an iOS launch-screen advertisement in minutes](<https://allluckly.cn/en/journal/2016-06-12-adding-a-launch-advertisement-to-an-ios-app/>)

