深度解析 Delegate 和 Notification 和 KVO

本文详细探讨了iOS开发中Delegate、Notification和KVO三种常见传值模式的特点与应用场景,并对比了它们之间的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

Delegate 和 Notification 和 KVO 是几种常见的传值模式。

在我们实际的开发中经常会用到。

在一些iOS的面试中也是经常被提及,也是衡量一个iOS水平的指标。

那么,这种模式的应用场景是什么?iOS-SDK内部是如何实现的? 它们之间的区别又是什么?

这篇文章中,我会为大家娓娓道来。

Delegate (代理,委托,协议)

我总结的Delegate的特点如下:

  • 1对1的传值
  • 支持正向与反向传值 (参数,返回值)
  • 可以用require和optional来修饰的

在delegate中,我们经常会看到 should 字眼, 我们以UITextFieldDelegate举例:

通常这样的情况,都会有个返回值。

这就是所谓接受者的态度。你可以在Controller里通过实现UITextFieldDelegate来控制TextFild的Return键是否有响应。

在UITextFaild内部一定存在这样的代码:

- (void)xxxxMethod
{
    //看看delegate实现时的返回值
    BOOL isOK = [_delegate textFieldShouldReturn:self];
    if (isOK) {
        
    } else {
        
    }
}
复制代码

同时,在这个例子中,我们也看到了,正向与反向的传值。

Controller通过实现UITextFieldDelegate方法,得到了参数UITextFaild,同时处理了一个BOO类型的返回值给UITextField。


同样的例子还有很多, 比如我们常见的UITableView。

有些朋友看到DataSource懵了, 其实不管叫什么都是Delegate.

再来看两个核心的delegate方法:

  • 你的controller告诉tableview有多少行
  • 你的controller告诉tableview绘制的Cell是什么样的

这些都是通过返回值实现的。

注意:

所以在用delegate时,我们在用delegate时,除了正常的通过参数传值,还要灵活的去运用返回值。


Notification (通知,消息)

我总结Notification有如下特点:

  • 1对N (多)
  • 不关心返回值,单向的传值
  • NSNotificationCenter单例统一处理发通知
  • 通过不同的唯一的通知标识名NotificationName区分不同通知
  • 被观察者主动发出通知

使用Notification一定要谨慎,由于1对多的缘故,避免滥用,不好查问题。

其次就是在Controller销毁时,一定要注销掉通知。

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
复制代码

KVO

  • 1对N (多)
  • 只能监听对象的属性变化 (比较局限)
  • 只有通过getters和setters来改变值才会触发KVO
  • 被观察者不用添加任何代码(比如发送通知),与被观察者完全解耦
  • 注册观察者时,属性名都是通过NSString来查找,容易出错
  • KVO的要求是对象必须能支持kvc机制 (NSObject的子类)
  • 单向传值

KVO原理

验证KVO的原理很复杂,网上文章很多,也可以自己写demo去验证,我这里就简单的说明:

首先,你要注册一个观察者,观察一下tableview的偏移量:

//添加监听者
[self.tableView addObserver: self forKeyPath: @"contentOffset" options: NSKeyValueObservingOptionNew context: nil];
复制代码

此时,编译器会修改监听对象(tableView)的isa指针,让这个指针指向一个新生成的(官方文档提及的)中间类。

其实这个中间类,就是带有NSKVONotifying_前缀的tableView的子类。

然后,它要通过setter,getter等方法,检测一下你注册的这个属性(contentOffset)是否存在有效?

不存在就直接抛异常崩溃了!

在重新实现setter方法的时候,有两个重要的方法:willChangeValueForKey和didChangeValueForKey,分别在赋值前后进行调用。

此外,还要遍历所有的回调监听者,然后通知这些监听者。

这时作为观察者,我们可以得到对象属性的变化值

- (void)observeValueForKeyPath:(NSString *)keyPath 
                      ofObject:(id)object    
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change 
                       context:(void *)context
{
    
}
复制代码

所有的监听者通过动态绑定的方式将其存储起来,但这样也会产生强引用,所以要释放

- (void)dealloc
{
    [self.tableView removeObserver: self forKeyPath: @"contentOffset"];
}
复制代码

这里只是抛砖引玉,其实iOS-sdk中的实现远不止这些,还要复杂的多,多了解底层的原理,学习好的思路,有助于我们写出更优秀的代码。

FAQ
  1. 为什么带NSKVONotifying_前缀?

    避免重复生成(你可能监听N个属性)

  2. 如何动态生成类?

    看Runtime!


运用场景

我举几个简单的例子,供参考:

用过QQ,微信的对这张图不陌生

这里会实时更新最新的一条聊天记录,包括内容,时间,未读数等等。

我们假设这是一个MessageItem对象。

在Cell内部,监听一下个MessageItem的content,time,unread等几个属性,发生变化时给cell的label控制赋值即可。


其它场景还有很多,比如新闻类的APP,在列表中,读过的都是浅灰色的背景,可以监听下unread属性。


写了KVO又不得不提KVC,后面我会专门写一篇文章,专门讲解KVO与KVC的知识。

还有一点,Runtime有空一定要多学习!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值