news 2026/9/10 1:54:13

Delphi 12 中 FMX ListView 的高性能跨平台列表实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Delphi 12 中 FMX ListView 的高性能跨平台列表实现

简介:Delphi 12 FMXUI 源码示例包围绕 FireMonkey 跨平台界面开发展开,重点演示 ListView 在数据绑定、ItemObjects 自定义视图、点击与焦点事件、分组折叠、滚动动画等方面的灵活用法。资源压缩包共 224 个文件,约 4.24MB,包含 100 个 pas 单元、54 个 fmx 窗体、33 张 png 图片素材,以及 dproj、dpr 工程配置、说明文档和授权文件,目录结构完整,便于直接打开工程对照学习。已有 1122 人学习下载。通过 YxdUI、FMXUI、Layout、ModernList 等多个可运行示例,开发者可以直观理解 TListView 的数据源接入、自定义列表项渲染和 Adapter 扩展逻辑,掌握 FMX 界面框架的模块组织方式,适合希望快速提升跨平台 UI 开发效率的 Delphi 入门与进阶开发者,是一份兼顾原理与实操的 FMXUI 学习资料。

1. 项目概述:Delphi 12 里那个被低估的 FMX ListView

看到这个标题,估计不少老Delphi用户会心一笑。说实话,以前提到 FireMonkey 的 ListView,很多人的第一反应是“卡”“不好用”,尤其是从 VCL 转过来的人,总觉得这组件不如原生 ListView 顺手。但 Delphi 12 这代 FMXUI 的 ListView 确实改头换面了,我最近在一个移动端项目中重度使用它,实测下来滚动流畅度、内存占用都比预期好很多,已经可以作为主力列表组件来用了。

这个组件能解决什么问题?简单说:你需要在 Android、iOS、Windows、macOS 上做一套界面差不多的列表页,要求滑动顺滑、数据动态加载、不同行有不同样式,那么 FMX 的 TListView 就是官方给的最优解。它内置了适配器(Adapter)机制、动态外观(DynamicAppearance)、视觉状态管理、懒加载回调,很多以前要自己造轮子的活儿,现在拖拖控件、写几行代码就搞定了。

这篇文章适合谁看?正在做 Delphi 跨平台项目的同学,尤其是想用 FMX 列表替换传统 ListBox 或者打算给老项目升级到 Delphi 12 的朋友。读完你能搞清楚 ListView 的运行机制、常用的开发套路、以及几个从文档里翻不出来的性能杀手。

2. FMX ListView 的整体设计思路:为什么它比旧方案强这么多

2.1 核心优势解析:从“控件”到“虚拟化容器”的转变

老版本 FMX 里,大家喜欢用 TListBox 来做列表,因为它简单——加几个 TListBoxItem,往里面塞控件就行。但问题也出在这里:如果你在 TListBoxItem 里放了多个控件,每条记录对应一个真实存在的组件实例,1000 条数据就是 1000 个 TLable、1000 个 TImage、1000 个 TProgressBar,这在移动端直接就卡成 PPT 了。

而 FMX 的 TListView 不一样,它在底层引入了一套类似于“视图回收”的机制。你看到的每一个列表项,并不是一个完整的、始终存在的控件树,而是通过适配器按需创建的。配合 DynamicAppearance 模式,你可以在一个 TListItem 上同时显示多行文本、图片、进度条、自定义按钮,但它内部其实是在几个预定义的“外观层”上做绘制,而不是真的创建出几十上百个 FMX 控件实例。用大白话说:旧方案是每个座位坐一个真人,ListView 方案是靠“影子分身”来回切换。

这套机制带来的最直接收益就是内存占用大幅下降。我实测过,在 Android 模拟器上加载 5000 条联系人记录,每个记录包含头像、两行文本和一个状态标签,FMX TListView 的内存基本稳定在 50MB 以内;同样的数据量放到 ListBox 上,分分钟突破 150MB 并且滚动时掉帧严重。Delphi 12 在底层的渲染管线又做了进一步优化,让这套机制的流畅度又往上走了一截。

2.2 方案选型背后的取舍:为什么不用自定义绘制或第三方组件

我见过很多朋友为了解决列表卡顿问题,第一反应就是“干脆写一个自绘列表吧”。这个思路确实可行,FMX 的 TListView 本身就提供了非常底层的绘制接口——你可以完全接管每一项的绘制逻辑,Unity 里叫“自绘 UI”,在 FMX 里就是重写整个 ListView 的渲染。但这样做的代价是你需要自己处理触摸事件、点击热区、文本换行、图片裁剪、状态动画等一大堆细节,工作量非常可观。

第三方组件库如 TMS FMX 的 ListView 系列也很优秀,但一方面需要额外付费,另一方面增加了一层学习成本。我的建议是:除非你的列表长得很离谱(比如每一行都是完全不同的自定义布局),否则优先用官方的 TListView + DynamicAppearance 就够了。Delphi 12 对这套机制的支持已经非常完善,包括 Visualizer(可视化视觉)、多状态外观切换,甚至和 TMultiView 一起做侧滑导航菜单都毫无压力。

而且 FMX TListView 的数据绑定能力在 Delphi 12 中也得到了增强。配合 TListView.Searchable、TListViewPullToRefresh 这类内置属性,你可以很轻松地实现“下拉刷新、实时搜索过滤”这两大移动端高频需求,不需要再额外引入一大堆数据操作逻辑。这就是为什么我在新项目中果断选择了 TListView 而不是反复纠结回退到旧方案。

3. 核心细节解析与关键配置:花半小时搞懂这几个概念,省下三天的坑

3.1 DynamicAppearance 模式:列表性能的生死线

FMX ListView 之所以快,最大的功臣是 DynamicAppearance(动态外观)。这是一种特殊的列表项模式,它把“一行列表项”拆分成若干个可独立控制的“视觉区块”。比如你想实现“头像 + 标题 + 副标题 + 右侧日期”这种最常见的布局,在传统 ListBox 里你需要拖 4 个控件进去;而在 DynamicAppearance 模式下,你只需要在 ListView 的 ItemAppearance 属性里选择 DynamicAppearance,然后定义几个 TListItemText、TListItemImage 即可。

这里有一个非常关键的设计思想:这些视觉区块和列表项本身并不是一对一的,而是由同一个模板类(TListItemDynamicAppearance)动态绘制出来的。也就是说,当你定义一个 TListItemText 用来显示用户昵称时,所有行上的“昵称”都共用同一条绘制逻辑,而不是各自独立持有文本控件。最终效果就是:即便你有 10000 条数据,内存里虚拟出来的真实“可视化对象”也就那么几十个,滚动时不断复用。

具体操作路径是这样:

  1. 在 Form 上放一个 TListView,把 ListView1.ItemAppearance.ItemAppearance := TListItemAppearance.DynamicAppearance;
  2. 右键控件,在“Edit DynamicAppearance”中点击“Add Item”;
  3. 添加 TListItemText,命名比如“txtTitle”,设置 PlaceOffset(偏移)和 PlaceSize(尺寸);
  4. 继续添加 TListItemImage,命名比如“imgAvatar”,设置尺寸和位置。

然后,在代码中通过ListView1.Items.AddItem('用户1', '你好,欢迎', '', '', 0, 0)这样一条语句就可以快速添加基础项,再通过item.Data['txtTitle'] := '用户1'为各个视觉区块赋值。你看,整个过程完全不需要实例化任何控件,性能不爆炸才怪。

3.2 常用 Attributes 与 Visualizer:让行为细节从代码中解耦

FMX ListView 还有一个很容易被忽略但非常实用的设计:Attributes(属性标签)和 Visualizer(视觉化器)。

Attributes 可以理解成“附加在列表项上的自定义数据包”,它可以存放字符串、整数、图片等任意类型。比如你要做一个订单列表,每个订单有“订单号”“金额”“状态”,你可以把这些数据全部塞进 TListItem 的 Attributes 字段,然后在绘制样式时再取出来用。这样做的好处是:数据的存储和展示完全解耦,你甚至可以在多个 ListView 之间共享一套数据源,或者把同一份数据展示成完全不同的外观。

Visualizer 则解决的是“列表背景上的图形元素”问题。如果你想让列表行之间显示一条细线作为分隔,又不愿意在每个 Item 里加一个 TLine 控件,那就在 ListView 的 Visualizer 中添加一条分隔线,指定颜色和偏移即可。这套机制做出来的分隔线本质上只是“背景绘制”,根本不会随着每行数据创建实例,对于实现行高、间距和分隔效果非常优雅。

有一个细节我特别想说:在 DynamicAppearance 模式下,你可以将一个 TListItemText 的 Truth 值(即是否显示)绑定到某个数据字段上,从而实现“某些行显示日期,某些行不显示日期”的按需显示效果。这一点在移动端界面里非常实用,因为它避免了在每行数据里做 if-else 逻辑。很多 Delphi 新手会忽略这个设计,把简单的事情复杂化,白白浪费了 FMX 的强大能力。

3.3 数据源与 Adapter:别把数据组装写在控件的 OnUpdate 事件里

我可以负责任地说,见过的新手案例里十有八九就是这样干的:在 TListView 的 OnUpdateObjects 事件中,通过if AObject is TListItemText then ...来动态赋值,又把图片从数据库里查出来赋给 TListItemImage。这种写法看起来没毛病,实际上是在“绘制事件”里做数据 IO,性能损失极其明显,滚动时会出现明显的顿挫。

更合理的方式是利用 FMX 的 DataSource 机制。你既可以用 LiveBindings(实时绑定)把 TListView 和一个数据集绑定起来,也可以在内存里手动建立 TListView 的 Adapter。后者在复杂场景下会更灵活——你可以在加载数据时就安排好每个列表项需要展示的所有字段,党组织成TListItemData或直接操作TListView.Items,让列表控件只负责纯粹的展示。这样不仅处理逻辑清晰,而且当用户触发“下拉刷新”时,你只需要重新往 Items 里填充数据,不需要反复触发绘制事件去改控件的值。

说一句经验之谈:如果你发现 ListView 的滚动时不时卡顿一下,第一反应别去怀疑渲染性能,先检查有没有在 OnUpdateObjects 事件里写耗时的逻辑。通常把数据组装提前到列表填充阶段,卡顿问题就已经解决了一半。

4. 完整实操:从零到一搭建一个带搜索和下拉刷新的动态列表

4.1 项目背景与目标设定

直接上案例。以“任务管理中心”为例,列表页需要展示任务名称、负责人、进度百分比、截止日期,并且支持下拉刷新与关键词过滤。运行平台至少覆盖 Android 和 Windows。这是移动办公类 App 最常见的场景,用 FMX TListView 来做非常典型。

先搭好界面骨架:一个 TLayout 作为顶部搜索栏(内含 TEdit 和 TButton),下面是 TListView,设置 Align:=Client。这个结构在手机和桌面上都能自适应。为了显示进度条,要在 DynamicAppearance 中添加一个 TListItemProgressBar,并将 PlaceOffset.Y 设置在副标题的下方。配置完成后的视觉布局大致是:左上角头像,右侧两行文字,右侧下方一条进度条,最右边显示截止日期。

4.2 数据准备与填充:先列表后 UI

在 FormCreate 中模拟生成 2000 条任务数据。这里用到一个技巧:用一个 TList 泛型列表暂存数据,Record 里有 TaskName、OwnerName、DateText、Progress 四个字段。之后手动创建列表项:

var LItem: TListViewItem; LData: TTaskInfo; begin for LData in FTaskList do begin LItem := ListView1.Items.Add; LItem.Data['txtName'] := LData.TaskName; LItem.Data['txtOwner'] := LData.OwnerName; LItem.Data['pbProgress'] := LData.Progress; LItem.Data['txtDate'] := LData.DateText; end; end;

关键点来了:LItem.Data[...]里的键名必须和 DynamicAppearance 中定义的属性名完全一致。如果你的文本组件在编辑界面里命名为 txtName,那这里的字符串就是 'txtName'。一个很反直觉但非常重要的事实是:ListView 在绘制时并不会自动根据你设置的键名去寻找对应组件,而是通过内部的字符串哈希表快速匹配并写入对应属性。所以这个名字一旦写错,往往没有任何编译报错或运行异常,就是界面上什么都不显示。我遇到过的这种“静默失败”场景不下十次,排查起来非常折磨人。

图片赋值稍微特殊一点。TListItemImage 的显示需要给 TListItemImage.Bitmap 赋值,但通过 Data 机制,你可以直接传一个 TBitmap 实例:

LItem.Data['imgAvatar'] := FBitmapDict[LData.OwnerName];

这里有个大坑就是:TListItemImage在多次绘制时会反复取内存中的 Bitmap,如果你用的是同一个窗体本地变量,可能没事;但如果图片是从异步网络加载的,必须确保在事件回调中给 item 的 Data 赋值后调用ListView1.Invalidate来强制刷新该行。很多朋友说“网络图片加载不出来”,排查到最后往往就是这一点没处理好。

4.3 搜索与下拉刷新:两种高频交互的实现

搜索框的逻辑非常直观,直接在 TEdit 的 OnChange 事件里过滤。为了性能考虑,不建议每次敲一个字符就重新添加整个 ListView 的 Items,而是用ListView1.BeginUpdate; ListView1.Items.Clear; ...; ListView1.EndUpdate;把整批刷新包起来。实测在 2000 条数据下,这样批量刷新一次的耗时在 Windows 上约 25ms,在 Android 真机上约 80ms,完全可以接受。

下拉刷新是通过ListView1.PullToRefresh := True以及OnPullToRefresh事件实现的。在这个事件中,你只需要重新加载数据、填充列表,然后给ListView1.PullToRefreshPendingFalse,指示器就会自动收起。这里提醒一句:在这个事件处理函数里不要做耗时太长的同步操作,比如从数据库读 5000 条记录。正确姿势是把耗时操作放到 TThread 或使用 Delphi 12 引入的并行编程库,等数据准备完成后再回到 UI 线程去更新 ListView。

我通常写一个LoadTasksFromDB方法,内部用 TThread.CreateAnonymousThread 执行数据加载,然后用TThread.Synchronize回到主线程更新 ListView。这样下拉刷新手势执行完,UI 线程几乎零阻塞,动画效果也很自然。Delphi 12 在 Android 上对主线程阻塞做了更严格的检测,如果你在 UI 线程里跑超过 100ms 的操作,系统可能直接弹 ANR(应用无响应)提示框。这是 Delphi 12 下比较容易踩到的新坑,值得注意。

4.4 滚动性能调优:Delphi 12 的隐藏升级

Delphi 12 的 TListView 在底层引出了几个新的性能特性。最直观的一个是TListView.ScrollViewport的惯性滚动算法改进。旧版本在 Windows 上用鼠标滚轮滚动 ListView,手指一抖页面就飞出去老远,体验非常诡异;Delphi 12 对鼠标滚轮和触摸滚轮的响应做了区分,现在鼠标滚轮一次滚动一个固定距离,触摸滑动则按照手指速度做惯性动画,更像原生 App 的手感。

另一个隐藏升级是TListView.EnableScrollBounce。这属性在 iOS 上默认开启,在 Android 上也不再是摆设。开启后,当你把列表拖到底部时,会出现一个有回弹效果的阻尼动画,和真实移动端 App 的交互一致。不要小看这个小动画,它对用户体验的提升是质变级别的。

如果你想要更激进的性能表现,还可以把TListView.ItemAppearance.ItemHeight设置为一个固定值(而不是 DynamicAppearance 的 auto-height)。这样排序、布局、索引计算全部变成 O(1) 级别的常量操作,渲染时能省掉大量的浮点运算。代价是你得保证每一行内容都差不多高。如果内容会换两行,就老老实实用 auto-height,否则会看到内容被裁切。这个度需要根据业务形态来权衡。

5. 常见问题与踩坑实录:这些都是文档里查不到的

5.1 图片资源释放:为何运行一会儿内存就暴涨

这个坑我印象太深了。当时做一个设备巡检应用,设备列表每一项都有一张现场照片。我开始的设计是:从网络下载图片后直接item.Data['imgThumb'] := ABitmap,然后就不再管。跑了几分钟后,App 内存以肉眼可见的速度往上飙,最后直接闪退。

后来排查发现,FMX 的 TListItemImage 在未设置OwnsBitmap := False时,默认会接管 LoadFromStream 或通过 Data 赋值的 Bitmap 生命周期。当列表项被回收时,那个 Bitmap 对象会被释放。但如果同一张图片被多个 item 同时引用,其中一个 item 被回收,整个 Bitmap 就变成野指针,导致内存泄漏甚至访问冲突。

解决办法有两条路。第一是在外部维护一个全局 Bitmap 缓存字典,ListView 里的TListItemImage.Bitmap只持有同一个缓存实例的引用,并把OwnsBitmap置为 False。第二是使用 FastMM4 的泄漏报告功能在退出前检查,看是否有 TBitmap 对象始终没有释放。这两个方案在我实际项目中都用过,方案一在“图片总量少、复用率高”的场景下更稳;方案二适合“图片多且每个 item 图片不同”的富媒体场景。

5.2 动态外观改属性不生效:先检查 Name 再考虑缓存问题

在 FMX ListView 中调试“为什么某行文本没显示”是一件容易让人头秃的事。有次调试一个实时股票行情页面,某个字段的行情涨跌幅总是显示不出来,图文代码逻辑看起来都没问题,后来发现问题出在 DynamicAppearance 里那个属性的名字带了前导空格。

这是一个极其隐蔽的坑:在动态外观编辑器里,你输入属性名时如果手滑在名字前打了一个空格,它在设计期看着完全正常,但在代码里通过Data['字段名']赋值时却永远匹配不上,因此那部分 UI 就永远是默认状态。遇到这种问题,先用代码强制给那个 Data 键赋一次值,然后在 OnUpdateObjects 里打断点看AObject的名称是否匹配。如果名称不匹配,十有八九就是名称有不可见字符。

还有一种情况是:当你修改完 DynamicAppearance 里的属性布局后,界面上没有任何变化。这不是 FMX 的 bug,而是因为列表项的视觉属性是在TListViewItem第一次被创建时根据模板生成并缓存的。如果你在运行过程中改变了某个属性的PlaceOffset.X,旧 item 不会自动更新视觉效果,需要先清空列表重建,或者调用ListView1.ItemAppearance.ResetAppearance

5.3 列表项点击区域太小:手滑误触的优化方案

移动端列表的另一种高频需求是点击整行触发跳转,但点击空白处或点击右侧图片时不希望触发。FMX TListView 在 DynamicAppearance 下,默认只在“文本区域”收到点击时才触发 OnClick 事件。当你在“头像 + 文本”布局中想让头像也成为点击区域时,要给头像这个 TListItemImage 设置HitTest := True属性。这个概念和 VCL 里的 HitTest 含义类似。

但是这里有一个副作用:如果你把多个子区域的 HitTest 都设为 True,那么点击行为会变得“只命中子区域”,而不是整行响应。所以实际开发中,我习惯把“透明背景”的 TListItemText 铺满整行,作为唯一 HitTest 区域,这样既能保证整行可点击,又不会干扰单独的图片点击事件。当你把点击事件逻辑拆到子区域之后,务必记得把主文本区域的 HitTest 设为 False,否则点击文本时会同时触发两个逻辑,导致跳转两次。

5.4 多线程更新 ListView:别忘掉 Synchronize 的本质限制

在 Delphi 12 中开发跨平台应用,很多人会用 TTask 或匿名线程去处理业务逻辑,比如网络请求、数据解析。这本身没问题,但如果你直接在子线程里调用ListView1.Items.Add或修改 Data 字段,轻则界面不刷新,重则直接崩在 Windows 的 RTL 层。

FMX 不是线程安全的,所有 UI 操作都必须回到主线程执行。常见的正确写法是:

TTask.Run( procedure var LDataList: TList<TTaskInfo>; begin LDataList := LoadDataFromService; TThread.Queue(nil, procedure begin MergeDataToListView(LDataList); end); end);

TThread.Queue而不是TThread.Synchronize的理由是:Queue 不会阻塞子线程,而是把操作放进主线程消息队列中排队异步执行;Synchronize 则会等待主线程执行完毕才返回,如果主线程在处理耗时操作,子线程会白白卡住。在数据加载量比较小的场景两者区别不明显,但如果是高频分页加载,Queue 的优势非常明显。这一点同样适用于拉动刷新后加载大量数据的情况。

5.5 纯字符串字典键引发的性能黑洞

这个话题正好呼应热词里的“delphi 字符串作字典key”。如果你用 TStringDictionary 或 TDictionary<string, ...> 存储列表数据,推荐使用 Delphi 12 默认的字符串哈希算法,它在大多数情况下已经足够好。但有个容易忽略的问题:当列表数据量大(超过大几千条)并且你不小心走了大小写敏感的重载,每查一次 key 都会做一次完整的大写转换,内存开销会明显放大。因此在给 ListView 的 Data 字段赋值时,应尽量保证字符串键的大小写稳定,不要一会儿用 'txtName',一会儿用 'TXTNAME',否则在内部哈希匹配时会产生额外的比较开销。

6. 最后的经验分享:从会用组件到用顺手的距离

我不是第一次跟别人说,FMX TListView 是一个值得投入时间研究的组件。Delphi 12 对 FMX 的调度和底层渲染管线的优化,让 TListView 的性能已经追平甚至超越了大多数原生跨平台方案。但真正决定它上限的还是你如何组织数据和绘制逻辑。

在我目前实际项目中,最常用的一个性能组合术是:外部用“轻量数据记录”缓存原始数据,ListView 的 Data 字段永远只保留展示所需的字段副本。这样不管是做搜索过滤、分组、排序还是切换视图,都只需要对新产生的临时数据集合做一次列表重建,而不需要在 ListView 的 internals 里反复修改。习惯这种思路之后,你会发现列表即使做到几万条数据,依然顺滑得像丝绸一样。

还有一个很容易被忽略的实用技巧:利用TListView.ItemIndex的返回值实现“定位到某一行”。当用户通过搜索找到目标后,需要滚动到那一行醒目显示,可以直接ListView1.ItemIndex := idx; ListView1.ScrollToItem(idx),这在旧版 FMX 中很不稳定,但在 Delphi 12 中表现很可靠。如果你想做“回到顶部”这类的操作,同样可以用这个方法。

最后想提的一点是,建议多去 Delphi 官方社区和 Embarcadero 的文档站翻一翻 TListView 的 Release Notes。Delphi 12 中修复了不少旧版本中“点击区域偏移”“图片闪烁”这类顽固问题,而且某些性能优化只在新版本中生效。如果你正在从旧项目升级,把 TListView 相关代码的测试用例准备好,升级后的收益会比想象中大得多。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 1:54:05

属性散射中心参数提取中的字典缩放算法解析与MATLAB实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 1:53:44

C#动态加载与反射机制详解:从Assembly.LoadFrom到Roslyn插件化开发

简介&#xff1a;面向C#开发者的动态加载与动态编译实例源码包&#xff0c;聚焦运行时加载外部程序集和动态生成代码两大核心场景&#xff0c;适用于插件系统、模块化应用、自定义规则引擎等需要灵活扩展的项目。资源包共含18个文件&#xff0c;以8个.cs源码文件为主体&#xf…

作者头像 李华
网站建设 2026/9/10 1:51:08

HashMap扩容机制深度解析:从JDK 1.7死循环到JDK 1.8优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 1:49:20

libcurl 连接阶段超时控制:CURLOPT_CONNECTTIMEOUT 完整解析

libcurl 连接阶段超时控制&#xff1a;CURLOPT_CONNECTTIMEOUT 完整解析 【免费下载链接】curl A command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, …

作者头像 李华
网站建设 2026/9/10 1:49:02

Redis String底层揭秘:SDS如何解决C字符串的三大痛点

1. 从一个诡异问题说起&#xff1a;String 在 Redis 里到底存的是什么我在排查线上问题时遇到过这么一件事&#xff1a;一个同事往 Redis 里存了一段带\x00的二进制数据&#xff0c;结果取出来发现后半段没了。他很困惑地问&#xff1a;“Redis 的 String 不是二进制安全的吗&a…

作者头像 李华