Delegate:什么是委托,委托模式和 iOS 协议

作者: IT Sectr 发布日期: 2026-02-17 阅读时间: 9 分钟

Delegate — 是一种设计模式,其中一个对象将任务的执行委托给另一个对象。在 iOS 中,该模式通过 Swift 协议和 @protocol Objective-C 实现。委托是 Cocoa Touch 的基本模式之一,用于 UITableViewDelegate、UITextFieldDelegate 以及数百个其他 Apple API 中。根据 Apple Developer Documentation (2025),大约 70% 的 UIKit 系统类使用委托来自定义行为而无需继承。

要点

  • Delegate — 对象 A 通过协议或接口将任务执行委托给对象 B 的模式
  • Swift 协议 定义了一组委托可以或必须实现的方法
  • 弱引用 (weak) 对委托是强制性的,以防止保留循环和内存泄漏
  • UITableViewDelegate — iOS 中最常见的委托示例,拥有 20+ 个可选方法
  • 与 Observer 的区别:委托是 1:1 关系,而通知遵循 1:N 原则

什么是 Delegate 模式?

Delegate — 是一种行为设计模式,允许对象将其部分职责转移给另一个对象。与继承(子类重写父类方法)不同,委托使用组合:所有者对象存储对委托的引用,并在特定点调用其方法。

委托定义了一个 协议 — 一组委托可以实现的方法。方法分为必需 (required) 和可选 (optional)。在 Swift 中,协议的可选方法用 @objc optional 关键字标记。

委托的工作原理

对象 A(所有者)包含 delegate 属性 — 对对象 B(委托)的弱引用。当发生事件时,A 检查 B 是否实现了相应的协议方法并调用它。弱引用 是强制性的:没有它,委托无法从内存中释放,因为所有者通过强引用持有它。

Swift
protocol LoaderDelegate: AnyObject {
    func loaderDidStart(_ loader: DataLoader)
    func loader(_ loader: DataLoader, didLoad data: Data)
    func loader(_ loader: DataLoader, didFailWith error: Error)
}

class DataLoader {
    weak var delegate: LoaderDelegate?
    
    func start() {
        delegate?.loaderDidStart(self)
        // 异步加载
    }
}

DataLoader 类定义了 LoaderDelegate 协议,并在加载生命周期的关键点调用委托的方法。AnyObject 保证协议只能由类实现 — 这对于弱引用是必需的。

Delegate 在 Swift 中如何工作?

委托的实现 在 Swift 中包括三个步骤:声明协议、在所有者中创建弱 delegate 属性、以及在委托类中实现协议。让我们通过一个带有验证的自定义 UITextField 示例来了解。

Swift
protocol ValidatorDelegate: AnyObject {
    func validate(_ input: String) -> Bool
    func validatorDidFail(_ input: String)
}

class ValidatedTextField: UITextField {
    weak var validator: ValidatorDelegate?
    
    override func textDidChange() {
        guard let text = self.text else { return }
        if validator?.validate(text) == false {
            validator?.validatorDidFail(text)
            self.layer.borderColor = UIColor.red.cgColor
        }
    }
}

class LoginViewController: UIViewController, ValidatorDelegate {
    let textField = ValidatedTextField()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        textField.validator = self
    }
    
    func validate(_ input: String) -> Bool {
        return input.count >= 6
    }
    
    func validatorDidFail(_ input: String) {
        print("验证失败:输入太短")
    }
}

LoginViewController 实现了 ValidatorDelegate 协议,并将自己设置为 textField 的委托。每次文本更改时,ValidatedTextField 调用 validate(_:),如果验证失败 — 则调用 validatorDidFail(_:)。Controller 充当 View 和验证逻辑之间的中介。

Delegate vs NotificationCenter vs Closure

在委托、通知和闭包之间的选择 取决于接收者的数量和组件的耦合程度。每种机制都解决了对象之间的通信问题,但有不同的权衡。

特性DelegateNotificationCenterClosure
连接类型1:11:N1:1
耦合度弱(通过协议)非常弱(字符串键)中等(捕获上下文)
类型安全完整无 (Any?)完整
保留循环风险无 (weak)有 (捕获 self)
何时使用具有多个方法的复杂回调许多人感兴趣的事件具有 1-2 个回调的简单闭包

Delegate 在需要将一系列相关事件传递给一个接收者时是最优的。NotificationCenter 更适合广播通知。Closure — 用于简单的异步操作,例如 URLSession 中的 completion handler。

在 Objective-C 中实现 Delegate

Objective-C 使用 @protocol 和 @optional 来声明委托。与 Swift 不同,协议的所有方法默认都是可选的。关键区别 — 在向委托发送消息之前调用 respondsToSelector:,因为该方法可能尚未实现。

Objective-C
@protocol ImageCacheDelegate 
@optional
- (void)cacheDidStartDownload: (ImageCache *)cache;
- (void)cache: (ImageCache *)cache didCacheImage: (UIImage *)image;
@required
- (void)cache: (ImageCache *)cache didFailWithError: (NSError *)error;
@end

@interface ImageCache : NSObject
@property (nonatomic, weak) id<ImageCacheDelegate> delegate;
- (void)downloadImageAtURL: (NSURL *)url;
@end

@implementation ImageCache
- (void)downloadImageAtURL: (NSURL *)url {
    if ([self.delegate respondsToSelector:@selector(cacheDidStartDownload:)]) {
        [self.delegate cacheDidStartDownload:self];
    }
    // 异步图像加载
}
@end

Objective-C 的关键区别:在调用可选方法之前,需要检查 respondsToSelector:。在 Swift 中,协议的可选方法消除了此检查 — optional chaining (?.) 自动处理缺少实现的情况。

使用委托时的常见错误

使用委托的错误 会导致内存泄漏、应用程序崩溃和不明显的错误。让我们看看五个最常见的问题。

对委托的强引用

保留循环 — 最常见的错误。如果 delegate 属性被声明为 strong,而委托又拥有所有者对象,就会形成保留循环。两个对象永远不会从内存中释放。解决方案:在 Swift 中始终将委托声明为 weak var,在 Objective-C 中声明为 @property (weak)

委托在 dealloc 后未置空

如果所有者对象比委托存活得更久,并且引用仍然存在,调用委托方法将导致 EXC_BAD_ACCESS。弱引用会自动解决此问题:释放委托后,属性变为 nil。然而,在多线程场景中,建议也在主线程中检查委托。

协议中方法数量过多

具有 20+ 个方法的协议违反了接口隔离原则 (ISP)。UITableViewDelegate 包含大约 30 个可选方法 — 这是一个历史例外。在您自己的协议中,最好将责任划分为多个小协议,每个协议有自己的角色。

iOS 系统 API 中的 Delegate 示例

Apple 系统 API 积极使用 Delegate 模式。让我们看看 UIKit 中三个关键示例,它们出现在每个 iOS 应用程序中。

API协议关键方法
UITableViewUITableViewDelegatedidSelectRowAt, heightForRowAt, willDisplay
UITextFieldUITextFieldDelegateshouldChangeCharactersIn, didBeginEditing, shouldReturn
URLSessionURLSessionDelegatedidReceiveChallenge, didCompleteWithError, didBecomeInvalidWithError

这些协议中的每一个都实现了行为的不同方面:UITableViewDelegate 管理外观和对触摸的响应,UITextFieldDelegate 控制文本输入,URLSessionDelegate 处理网络事件。这展示了该模式的灵活性:委托可以适应任何责任领域。

常见问题

iOS 中 delegate 和 dataSource 有什么区别?

Delegate 管理行为和外观(单元格高度、触摸响应)。DataSource 提供数据(行数、单元格内容)。UITableViewDelegate 和 UITableViewDataSource — 这是两个独立的协议,分担展示和数据的责任。

为什么 delegate 被声明为 weak?

弱引用 防止保留循环。所有者(例如 UITableView)只以 weak 方式持有对委托的引用。如果委托(UIViewController)拥有表格,对委托的强引用将创建一个循环:ViewController → UITableView → Delegate (ViewController)。Weak 打破了这个循环。

可以在 SwiftUI 中使用 delegate 吗?

SwiftUI 中,Delegate 模式较少使用 — 它被 @Binding、@State 和闭包取代。然而,delegate 仍然用于通过 UIViewRepresentable 集成 UIKit。例如,MKMapViewDelegate 和 WKUIDelegate 在将 UIKit 组件包装到 SwiftUI 中时仍然相关。

Swift 协议中的 @objc optional 是什么?

@objc optional 允许在 Swift 协议中声明可选方法。这是与 Objective-C 运行时的兼容性机制。没有 @objc,Swift 协议的所有方法默认都是必需的。Optional 用于 UIKit 协议中,委托可以只实现需要的方法。

一个对象可以有多少个委托?

一个对象对于每个 delegate 属性只能有 一个委托。如果需要通知多个对象,请使用 multicast delegate、委托数组或 NotificationCenter。Delegate 模式最初被设计为 1:1 关系。

总结

  • Delegate — 一种行为设计模式,通过协议将任务的执行从一个对象传递到另一个对象
  • Swift 协议 声明一组方法;可选方法用 @objc optional 标记,必需方法总是被实现
  • 弱引用 对委托是强制性的,以防止保留循环和内存泄漏
  • UITableViewDelegate — 最著名的示例;其方法管理单元格的高度、选择和显示
  • Delegate 与 NotificationCenter 的区别在于 1:1 关系对比 1:N 以及完整的类型安全性
  • Objective-C 需要对可选方法进行 respondsToSelector: 检查,Swift 自动处理
  • 建议:对具有多个相关方法的复杂回调使用 delegate,对简单闭包使用 closure

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读