iOS中的KVO(Key-Value Observing)详解
iOS中的KVO(Key-Value Observing)详解
一、KVO概述
KVO(Key-Value Observing),即键值观察/监听,是苹果提供的一套事件通知机制。它允许一个对象(观察者)观察/监听另一个对象(被观察者)指定属性值的改变。当被观察对象的属性值发生变化时,KVO会自动触发监听方法来通知观察者。这种机制在MVC(Model-View-Controller)应用程序中的各层之间进行通信时特别有用,是实现观察者模式的一种重要方式。
二、KVO的作用
KVO的主要作用在于提供一种非侵入性的方式来监听对象属性的变化。它不需要修改被观察对象的内部代码,只需在观察者中注册对特定属性的监听即可。当被观察的属性值发生变化时,KVO会自动通知观察者,使得观察者能够做出相应的响应。这种机制在以下场景中尤为有用:
-
UI自动更新:在iOS开发中,UI界面的更新往往依赖于后台数据的变化。通过KVO,开发者可以轻松地监听数据模型(Model)中相关属性的变化,并在属性值发生变化时自动更新UI控件,从而实现数据的实时展示。
-
缓存管理:在应用程序中,缓存是提高性能的重要手段。然而,当缓存中的数据发生变化时,需要确保与之相关的其他数据或UI界面也得到及时更新。通过KVO,开发者可以监听缓存对象属性的变化,并在变化发生时进行相应的处理,如更新缓存、通知其他对象等。
-
依赖属性更新:在某些情况下,一个属性的值可能依赖于另一个或多个属性的值。例如,在一个矩形类(Rectangle)中,面积(area)属性就依赖于宽度(width)和高度(height)属性。通过KVO,开发者可以监听这些依赖属性的变化,并在变化发生时重新计算并更新依赖属性的值。
-
监听网络请求:在iOS开发中,网络请求是获取数据的重要途径。然而,网络请求的结果往往是不确定的,且可能受到多种因素的影响。通过KVO,开发者可以监听网络请求对象的状态变化(如请求成功、请求失败等),并在状态变化时更新UI界面或进行其他处理。
三、KVO的使用场景
KVO的使用场景非常广泛,几乎在任何需要监听对象属性变化的场景中都可以使用。以下是一些具体的使用场景示例:
-
用户信息更新:在社交应用中,用户的个人信息(如昵称、头像等)可能会随时发生变化。通过KVO,开发者可以监听用户信息对象的属性变化,并在变化发生时更新UI界面,如用户头像、昵称等。
-
购物车商品数量变化:在电商应用中,购物车中的商品数量可能会随着用户的操作(如添加商品、删除商品等)而发生变化。通过KVO,开发者可以监听购物车对象中的商品数量属性变化,并在变化发生时更新购物车图标、商品列表等UI界面。
-
视频播放进度:在视频播放应用中,视频的播放进度是一个重要的属性。通过KVO,开发者可以监听视频播放对象的播放进度属性变化,并在变化发生时更新播放进度条、剩余时间等UI界面元素。
-
系统设置变化:在iOS系统中,系统设置(如音量、亮度等)的变化可能会影响应用程序的表现。通过KVO,开发者可以监听系统设置对象的相关属性变化,并在变化发生时调整应用程序的表现(如调整音量大小、亮度等级等)。
四、KVO的实现原理
KVO的实现原理相对复杂,主要涉及到运行时(Runtime)的一些特性。当某个对象(被观察者)的属性被注册为观察对象时,KVO会在运行时动态地创建一个该对象的子类(命名规则通常为NSKVONotifying_xxx),并将该子类的isa指针指向原对象。这个子类会重写被观察属性的setter方法,并在setter方法中实现通知机制。当被观察属性的值发生变化时,会调用这个重写后的setter方法,进而触发KVO的通知机制。
具体来说,KVO的通知机制包括以下几个步骤:
-
注册观察:通过调用被观察对象的
addObserver:forKeyPath:options:context:方法注册观察者和要观察的属性。 -
属性变化:当被观察的属性值发生变化时(通常是通过setter方法或KVC赋值),会触发重写后的setter方法。
-
发送通知:在重写后的setter方法中,会调用
willChangeValueForKey:和didChangeValueForKey:方法来通知观察者属性值即将发生变化和已经发生变化。这两个方法会触发KVO的监听回调方法observeValueForKeyPath:ofObject:change:context:。 -
执行回调:在
observeValueForKeyPath:ofObject:change:context:方法中,观察者可以获取到变化的属性名(keyPath)、变化的对象(object)、变化前后的值(change)以及上下文信息(context,如果注册时提供了)。根据这些信息,观察者可以执行相应的操作来响应属性的变化。
五、KVO的优缺点
优点:
- 非侵入性:KVO允许在不修改被观察对象代码的情况下进行监听,这有助于保持代码的解耦和可维护性。
- 自动通知:当被观察的属性值发生变化时,KVO会自动通知所有注册的观察者,无需手动触发通知。
- 灵活性:可以观察对象的几乎任何属性,只要这些属性是通过setter方法或KVC可访问的。
- 支持多种属性:一个观察者可以同时观察多个对象的多个属性,这使得在复杂的应用程序中管理属性变化变得更加容易。
缺点:
- 性能开销:KVO机制的实现涉及到运行时(Runtime)的动态类创建和方法重写,这可能会带来一定的性能开销。虽然对于大多数应用来说这种开销是可以接受的,但在性能敏感的应用中需要谨慎使用。
- 内存管理复杂:在使用KVO时,需要注意内存管理的问题。观察者需要确保在不再需要监听属性变化时及时注销观察,以避免内存泄漏。
- 错误难以追踪:由于KVO的回调方法
observeValueForKeyPath:ofObject:change:context:是通用的,并且可能由多个不同的属性变化触发,因此当出现问题时可能难以追踪到具体的属性变化源。 - 不支持自定义setter:如果属性的setter方法是自定义的,并且没有调用
willChangeValueForKey:和didChangeValueForKey:方法,那么KVO机制将无法正常工作。
六、KVO的最佳实践
- 明确观察目标:在注册观察之前,明确你需要观察哪些对象的哪些属性。避免无谓的观察,以减少性能开销和内存使用。
- 及时注销观察:在观察者不再需要监听属性变化时,及时调用
removeObserver:forKeyPath:方法注销观察。这有助于避免内存泄漏和不必要的通知。 - 使用上下文信息:在注册观察时,如果可能的话,使用上下文信息(context)来区分不同的观察。这样,在回调方法中就可以通过上下文信息来判断是哪个属性发生了变化。
- 谨慎处理回调方法:在
observeValueForKeyPath:ofObject:change:context:回调方法中,确保你能够正确处理所有可能的属性变化。同时,注意检查传入的参数,以避免因参数错误而导致的程序崩溃。 - 考虑替代方案:在某些情况下,KVO可能不是最佳的选择。例如,如果你只需要监听一个属性的变化,并且这个属性是由你自己控制的,那么你可以考虑使用代理(Delegate)或回调(Block)来实现。这些方案通常比KVO更简单、更直接,并且没有额外的性能开销。
七、KVO与其他技术的比较
KVO作为iOS开发中一种重要的通信机制,与其他技术(如通知(Notification)、代理(Delegate)、回调(Block)等)相比,有着自己独特的优势和适用场景。
-
与通知(Notification)比较:通知是一种更加通用的广播机制,它允许任何对象在任何时候发送消息给任何监听该通知的对象。然而,通知并不直接关联到特定的对象或属性,因此它可能不如KVO那样精确。此外,通知的发送者和接收者之间需要约定一个唯一的通知名称,这可能会增加代码的耦合度。
-
与代理(Delegate)比较:代理是一种更加直接和明确的通信方式,它允许一个对象(代理持有者)将某些任务或消息转发给另一个对象(代理)。代理通常用于定义一对一的关系,并且代理方法通常是可选的。然而,代理需要显式地定义代理协议和代理方法,这可能会增加代码的复杂度。此外,当需要监听多个对象的多个属性时,使用代理可能会变得非常繁琐。
-
与回调(Block)比较:回调是一种更加轻量级和灵活的通信方式,它允许将一个函数(或代码块)作为参数传递给另一个函数。回调通常用于定义任务完成后的行为,并且它们可以很容易地与异步操作结合使用。然而,回调可能会导致回调地狱(Callback Hell),即多层嵌套的回调导致代码难以阅读和维护。此外,当需要取消回调或管理多个回调时,可能会变得复杂。
综上所述,KVO作为iOS开发中一种重要的通信机制,在监听对象属性变化方面具有独特的优势。然而,在使用KVO时也需要注意其潜在的缺点和限制,并结合具体的场景和需求来选择最合适的通信方式。
相关文章:
iOS中的KVO(Key-Value Observing)详解
iOS中的KVO(Key-Value Observing)详解 一、KVO概述 KVO(Key-Value Observing),即键值观察/监听,是苹果提供的一套事件通知机制。它允许一个对象(观察者)观察/监听另一个对象&#…...
算法 —— 暴力枚举
目录 循环枚举 P2241 统计方形(数据加强版) P2089 烤鸡 P1618 三连击(升级版) 子集枚举 P1036 [NOIP2002 普及组] 选数 P1157 组合的输出 排列枚举 P1706 全排列问题 P1088 [NOIP2004 普及组] 火星人 循环枚举 顾名思…...
构造+有序集合,CF 1023D - Array Restoration
一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1023D - Array Restoration 二、解题报告 1、思路分析 先考虑合法性检查: 对于数字x,其最左位置和最右位置 之间如果存在数字比x小,则非法 由于q次操作,第q…...
Scrapy 爬取旅游景点相关数据(四)
本节内容主要为: (1)创建数据库 (2)创建数据库表 (3)爬取数据进MYSQL库 1 新建数据库 使用MYSQL数据库存储数据,创建一个新的数据库 create database scrapy_demo;2 新建数据表 CR…...
Vue常用指令及其生命周期
作者:CSDN-PleaSure乐事 欢迎大家阅读我的博客 希望大家喜欢 目录 1.常用指令 1.1 v-bind 1.2 v-model 注意事项 1.3 v-on 注意事项 1.4 v-if / v-else-if / v-else 1.5 v-show 1.6 v-for 无索引 有索引 生命周期 定义 流程 1.常用指令 Vue当中的指令…...
简化数据流:Apache SeaTunnel实现多表同步的高效指南
Apache SeaTunnel除了单表之间的数据同步之外,也支持单表同步到多表,多表同步到单表,以及多表同步到多表,下面简单举例说明如何实现这些功能。 单表 to 单表 一个source,一个sink。 从mysql同步到mysql,…...
均匀圆形阵列原理及MATLAB仿真
均匀圆形阵列原理及MATLAB仿真 目录 前言 一、均匀圆阵原理 二、圆心不存在阵元方向图仿真 三、圆心存在阵元方向图仿真 四、MATLAB仿真代码 总结 前言 本文详细推导了均匀圆形阵列的方向图函数,对圆心不放置阵元和圆心放置阵元的均匀圆形阵列方向图都进行了仿…...
vue2使用univerjs
1、univerjs Univer 提供了一个全面的企业级文档与数据协同的解决方案,支持电子表格、文本文档和演示幻灯片三大核心文档类型。通过灵活的 API 和插件机制,开发者可以在 Univer 的基础上进行个性化功能的定制和扩展,以适应不同用户在不同场景…...
VUE3 el-table-column header新增必填*
1.在需要加必填星号的el-table-column上添加render-header属性 <el-table-column :label"getName(产品代码)" :render-header"addRedStart" prop"MODELCODE" min-width“4.5%”> <template v-slot"scope"> <el-input …...
条件概率和贝叶斯公式
...
Kali中docker与docker-compose的配置
权限升级 sudo su 升级为root用户 更新软件 apt-get update安装HTTPS协议和CA证书 apt-get install -y apt-transport-https ca-certificates下载docker apt下载docker apt install docker.io 验证docker安装是否成功 查版本 docker -v 启动docker systemctl start …...
C++ | Leetcode C++题解之第283题移动零
题目: 题解: class Solution { public:void moveZeroes(vector<int>& nums) {int n nums.size(), left 0, right 0;while (right < n) {if (nums[right]) {swap(nums[left], nums[right]);left;}right;}} };...
Exponential Moving Average (EMA) in Stable Diffusion
1.Moving Average in Stable Diffusion (SMA&EMA) 1.Moving average 2.移动平均值 3.How We Trained Stable Diffusion for Less than $50k (Part 3) Moving Average 在统计学中,移动平均是通过创建整个数据集中不同选择的一系列平均值来分析数据点的计算。 …...
017、Vue动态tag标签
文章目录 1、先看效果2、代码 1、先看效果 2、代码 <template><div class "tags"><el-tag size"medium"closable v-for"item,index in tags":key"item.path":effect"item.title$route.name?dark:plain"cl…...
RocketMQ 架构概览
Apache RocketMQ 是一个分布式消息中间件和流计算平台,提供低延迟、高性能和可靠的队列服务,并且支持大规模的分布式系统。在详细介绍 RocketMQ 的整体架构之前,先了解其设计目标和核心特性是很重要的。RocketMQ 主要用于处理大规模的消息&am…...
优化医疗数据管理:Kettle ETL 数据采集方案详解
在现代医疗保健领域,数据的准确性、完整性和及时性对于提高医疗服务质量和患者护理至关重要。为了有效管理和利用医疗数据,Kettle ETL(Extract, Transform, Load)数据采集方案成为了许多医疗机构的首选工具之一。本文将深入探讨Ke…...
spring-from表单
在spring boot当中,from表单怎样开发(name=value) 先列出接口所需信息(抓包得到请求信息),将这些必要信息以注解的方式表达出来 步骤: 梳理前置条件(请求地址,请求header,请求方法,请求数据,响应结果)编辑一个普通类,在类上标记注解@Controller: 标记在类上,让类…...
【.NET】asp.net core 程序重启容器后redis无法连接,连接超时
环境是容器化部署asp.net core 程序当有大量请求打到容器如果此时重启容器会出现,redis无法连接情况。 使用 csredis 库报错: Status unavailable, waiting for recovery. Connect to server timeout 使用StackExchange.Redis 报错: Time…...
【vue前端项目实战案例】Vue3仿今日头条App
本文将开发一款仿“今日头条”的新闻App。该案例是基于 Vue3.0 Vue Router webpack TypeScript 等技术栈实现的一款新闻资讯类App,适合有一定Vue框架使用经验的开发者进行学习。 项目源码在文章末尾 1 项目概述 该项目是一款“今日头条”的新闻资讯App…...
常见的文心一言的指令
文心一言,作为百度研发的预训练语言模型“ERNIE 3.0”的一项功能,能够与人对话互动,回答问题,协助创作,高效便捷地帮助人们获取信息、知识和灵感。以下是一些常见的文心一言指令类型及其具体示例: 1. 查询…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
