当前位置: 首页 > news >正文

iOS中的KVO(Key-Value Observing)详解

iOS中的KVO(Key-Value Observing)详解

一、KVO概述

KVO(Key-Value Observing),即键值观察/监听,是苹果提供的一套事件通知机制。它允许一个对象(观察者)观察/监听另一个对象(被观察者)指定属性值的改变。当被观察对象的属性值发生变化时,KVO会自动触发监听方法来通知观察者。这种机制在MVC(Model-View-Controller)应用程序中的各层之间进行通信时特别有用,是实现观察者模式的一种重要方式。

二、KVO的作用

KVO的主要作用在于提供一种非侵入性的方式来监听对象属性的变化。它不需要修改被观察对象的内部代码,只需在观察者中注册对特定属性的监听即可。当被观察的属性值发生变化时,KVO会自动通知观察者,使得观察者能够做出相应的响应。这种机制在以下场景中尤为有用:

  1. UI自动更新:在iOS开发中,UI界面的更新往往依赖于后台数据的变化。通过KVO,开发者可以轻松地监听数据模型(Model)中相关属性的变化,并在属性值发生变化时自动更新UI控件,从而实现数据的实时展示。

  2. 缓存管理:在应用程序中,缓存是提高性能的重要手段。然而,当缓存中的数据发生变化时,需要确保与之相关的其他数据或UI界面也得到及时更新。通过KVO,开发者可以监听缓存对象属性的变化,并在变化发生时进行相应的处理,如更新缓存、通知其他对象等。

  3. 依赖属性更新:在某些情况下,一个属性的值可能依赖于另一个或多个属性的值。例如,在一个矩形类(Rectangle)中,面积(area)属性就依赖于宽度(width)和高度(height)属性。通过KVO,开发者可以监听这些依赖属性的变化,并在变化发生时重新计算并更新依赖属性的值。

  4. 监听网络请求:在iOS开发中,网络请求是获取数据的重要途径。然而,网络请求的结果往往是不确定的,且可能受到多种因素的影响。通过KVO,开发者可以监听网络请求对象的状态变化(如请求成功、请求失败等),并在状态变化时更新UI界面或进行其他处理。

三、KVO的使用场景

KVO的使用场景非常广泛,几乎在任何需要监听对象属性变化的场景中都可以使用。以下是一些具体的使用场景示例:

  1. 用户信息更新:在社交应用中,用户的个人信息(如昵称、头像等)可能会随时发生变化。通过KVO,开发者可以监听用户信息对象的属性变化,并在变化发生时更新UI界面,如用户头像、昵称等。

  2. 购物车商品数量变化:在电商应用中,购物车中的商品数量可能会随着用户的操作(如添加商品、删除商品等)而发生变化。通过KVO,开发者可以监听购物车对象中的商品数量属性变化,并在变化发生时更新购物车图标、商品列表等UI界面。

  3. 视频播放进度:在视频播放应用中,视频的播放进度是一个重要的属性。通过KVO,开发者可以监听视频播放对象的播放进度属性变化,并在变化发生时更新播放进度条、剩余时间等UI界面元素。

  4. 系统设置变化:在iOS系统中,系统设置(如音量、亮度等)的变化可能会影响应用程序的表现。通过KVO,开发者可以监听系统设置对象的相关属性变化,并在变化发生时调整应用程序的表现(如调整音量大小、亮度等级等)。

四、KVO的实现原理

KVO的实现原理相对复杂,主要涉及到运行时(Runtime)的一些特性。当某个对象(被观察者)的属性被注册为观察对象时,KVO会在运行时动态地创建一个该对象的子类(命名规则通常为NSKVONotifying_xxx),并将该子类的isa指针指向原对象。这个子类会重写被观察属性的setter方法,并在setter方法中实现通知机制。当被观察属性的值发生变化时,会调用这个重写后的setter方法,进而触发KVO的通知机制。

具体来说,KVO的通知机制包括以下几个步骤:

  1. 注册观察:通过调用被观察对象的addObserver:forKeyPath:options:context:方法注册观察者和要观察的属性。

  2. 属性变化:当被观察的属性值发生变化时(通常是通过setter方法或KVC赋值),会触发重写后的setter方法。

  3. 发送通知:在重写后的setter方法中,会调用willChangeValueForKey:didChangeValueForKey:方法来通知观察者属性值即将发生变化和已经发生变化。这两个方法会触发KVO的监听回调方法observeValueForKeyPath:ofObject:change:context:

  4. 执行回调:在observeValueForKeyPath:ofObject:change:context:方法中,观察者可以获取到变化的属性名(keyPath)、变化的对象(object)、变化前后的值(change)以及上下文信息(context,如果注册时提供了)。根据这些信息,观察者可以执行相应的操作来响应属性的变化。

五、KVO的优缺点
优点:
  1. 非侵入性:KVO允许在不修改被观察对象代码的情况下进行监听,这有助于保持代码的解耦和可维护性。
  2. 自动通知:当被观察的属性值发生变化时,KVO会自动通知所有注册的观察者,无需手动触发通知。
  3. 灵活性:可以观察对象的几乎任何属性,只要这些属性是通过setter方法或KVC可访问的。
  4. 支持多种属性:一个观察者可以同时观察多个对象的多个属性,这使得在复杂的应用程序中管理属性变化变得更加容易。
缺点:
  1. 性能开销:KVO机制的实现涉及到运行时(Runtime)的动态类创建和方法重写,这可能会带来一定的性能开销。虽然对于大多数应用来说这种开销是可以接受的,但在性能敏感的应用中需要谨慎使用。
  2. 内存管理复杂:在使用KVO时,需要注意内存管理的问题。观察者需要确保在不再需要监听属性变化时及时注销观察,以避免内存泄漏。
  3. 错误难以追踪:由于KVO的回调方法observeValueForKeyPath:ofObject:change:context:是通用的,并且可能由多个不同的属性变化触发,因此当出现问题时可能难以追踪到具体的属性变化源。
  4. 不支持自定义setter:如果属性的setter方法是自定义的,并且没有调用willChangeValueForKey:didChangeValueForKey:方法,那么KVO机制将无法正常工作。
六、KVO的最佳实践
  1. 明确观察目标:在注册观察之前,明确你需要观察哪些对象的哪些属性。避免无谓的观察,以减少性能开销和内存使用。
  2. 及时注销观察:在观察者不再需要监听属性变化时,及时调用removeObserver:forKeyPath:方法注销观察。这有助于避免内存泄漏和不必要的通知。
  3. 使用上下文信息:在注册观察时,如果可能的话,使用上下文信息(context)来区分不同的观察。这样,在回调方法中就可以通过上下文信息来判断是哪个属性发生了变化。
  4. 谨慎处理回调方法:在observeValueForKeyPath:ofObject:change:context:回调方法中,确保你能够正确处理所有可能的属性变化。同时,注意检查传入的参数,以避免因参数错误而导致的程序崩溃。
  5. 考虑替代方案:在某些情况下,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题移动零

题目&#xff1a; 题解&#xff1a; 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 在统计学中&#xff0c;移动平均是通过创建整个数据集中不同选择的一系列平均值来分析数据点的计算。 …...

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 是一个分布式消息中间件和流计算平台&#xff0c;提供低延迟、高性能和可靠的队列服务&#xff0c;并且支持大规模的分布式系统。在详细介绍 RocketMQ 的整体架构之前&#xff0c;先了解其设计目标和核心特性是很重要的。RocketMQ 主要用于处理大规模的消息&am…...

优化医疗数据管理:Kettle ETL 数据采集方案详解

在现代医疗保健领域&#xff0c;数据的准确性、完整性和及时性对于提高医疗服务质量和患者护理至关重要。为了有效管理和利用医疗数据&#xff0c;Kettle ETL&#xff08;Extract, Transform, Load&#xff09;数据采集方案成为了许多医疗机构的首选工具之一。本文将深入探讨Ke…...

spring-from表单

在spring boot当中,from表单怎样开发(name=value) 先列出接口所需信息(抓包得到请求信息),将这些必要信息以注解的方式表达出来 步骤: 梳理前置条件(请求地址,请求header,请求方法,请求数据,响应结果)编辑一个普通类,在类上标记注解@Controller: 标记在类上,让类…...

【.NET】asp.net core 程序重启容器后redis无法连接,连接超时

环境是容器化部署asp.net core 程序当有大量请求打到容器如果此时重启容器会出现&#xff0c;redis无法连接情况。 使用 csredis 库报错&#xff1a; Status unavailable, waiting for recovery. Connect to server timeout 使用StackExchange.Redis 报错&#xff1a; Time…...

【vue前端项目实战案例】Vue3仿今日头条App

本文将开发一款仿“今日头条”的新闻App。该案例是基于 Vue3.0 Vue Router webpack TypeScript 等技术栈实现的一款新闻资讯类App&#xff0c;适合有一定Vue框架使用经验的开发者进行学习。 项目源码在文章末尾 1 项目概述 该项目是一款“今日头条”的新闻资讯App&#xf…...

常见的文心一言的指令

文心一言&#xff0c;作为百度研发的预训练语言模型“ERNIE 3.0”的一项功能&#xff0c;能够与人对话互动&#xff0c;回答问题&#xff0c;协助创作&#xff0c;高效便捷地帮助人们获取信息、知识和灵感。以下是一些常见的文心一言指令类型及其具体示例&#xff1a; 1. 查询…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

宇树科技,改名了!

提到国内具身智能和机器人领域的代表企业&#xff0c;那宇树科技&#xff08;Unitree&#xff09;必须名列其榜。 最近&#xff0c;宇树科技的一项新变动消息在业界引发了不少关注和讨论&#xff0c;即&#xff1a; 宇树向其合作伙伴发布了一封公司名称变更函称&#xff0c;因…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...