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

Effective Objective-C 2.0 读书笔记——内存管理(下)

Effective Objective-C 2.0 读书笔记——内存管理(下)

在 dealloc 方法中只释放引用并解除监听

对象在经历其生命期后 ,最终会为系统所回收 ,这时就要执行dealloc 方法了。 在每个对象的生命期内,此方法仅执行一次,也就是当保留计数降为0的时候。在这个方法之中,主要就是释放对象所拥有的引用。

比如CoreFoundation 对象就必须手工释放,因为它们是由纯C的API 所生成的。 在dealloc方法中,通常还要做一件事,那就是把原来配置过的观测行为(observation behavior) 都清理掉。之前讲到通知传值就有说到,我们当不需要监听后就需要在dealloc函数当中将注册的通知中心进行销注。

系统不保证每个对象的 dealloc 都会在预定时机调用,特别是在应用终止时可能仍有对象存活,而操作系统会在程序退出后回收所有资源。对于那些开销较大或系统内稀缺的资源(例如文件描述符、套接字等),最好在对象不再需要时主动调用专门的“清理方法”(如 close),而不是等到 dealloc 时才释放。

以下是书中给出的例子:

-(void) close{//关闭资源_close = YES;}-(void)dealloc {if(!_close) {NSLog(@"数据库没有在dealloc之前关闭!!!");[self close];}}

有时候我们不只是想输出一条错误的消息,可能需要我们抛出对应的异常来表明我们这是一个严重的错误。

调用 dealloc 后,对象已经不再有效,任何进一步调用属性存取方法或其他业务逻辑的方法都可能导致错误。
为此,开发者应确保在 dealloc 中不调用那些可能依赖对象正常状态的操作。

编写 “异常安全代码” 时留意内存管理问题这一章节

在之前我们学习了书中关于如何抛出异常的方法,即使用@try…@catch…@finally或者NSError。这一章节主要讲了使用@try…@catch…@finally在MRC的情况下需要注意的一些点。

当发生异常时,内存管理需要特别关注,否则可能会导致内存泄漏。书中的这一段话强调了以下关键点:

  1. 异常可能导致对象泄漏
    try 代码块中,如果先对某个对象进行了 retainalloc 操作(增加了引用计数),但在释放它之前抛出了异常,而 catch 代码块未能正确处理该问题,那么该对象的内存将不会被释放,从而导致内存泄漏。
  2. C++ 对象的析构函数由 Objective-C 的异常处理机制执行
    在 C++ 中,每个对象都有一个析构函数(destructor),用于在对象生命周期结束时执行清理操作。而 Objective-C 的异常处理机制会确保异常发生时,C++ 对象的析构函数能够正确执行,以防止 C++ 对象在异常发生时泄漏。
  3. 系统资源(如文件句柄)更容易泄漏
    C++ 对象由于析构函数的存在,能在异常发生时得到一定程度的保护,但文件句柄、网络连接等系统资源没有自动管理机制。如果异常发生后没有手动清理这些资源,就会造成更严重的资源泄漏。因此,在处理这类资源时,应当使用 @try ... @finally 结构来保证清理操作一定会执行。
@try {EOCSomeClass *object = [[EOCSomeClass alloc] init];[object doSomethingThatMayThrow];[object release];
}
@catch (NSException *exception) {NSLog(@"Whoops, there was an error. Oh well...");
}

不难看出如果在执行doSomethingThatMayThrow如果抛出异常,那么就没有办法对obj进行release,那我们可以对代码进行修改

EOCSomeClass *object = [[EOCSomeClass alloc] init];
@try {[object doSomethingThatMayThrow];
}
@catch (NSException *exception) {NSLog(@"Whoops, there was an error. Oh well...");
}
@finally {[object release];
}

样可以保证无论是否发生异常,对象都能最终被释放,从而避免内存泄漏。

这里书中也介绍了一个“objc-arc-exceptions”这个编译器参数。ARC 主要依赖编译器自动生成内存管理代码,默认假定代码中不会使用异常。这样可以生成更高效的代码,因为编译器不必为异常清理路径额外插入内存释放代码。如果我们的程序一定需要使用@try…@catch…@finally

以弱引用避免保留环

ARC 引入了 strongweak 引用来简化内存管理:

  • strong:默认情况下,使用 strong 引用来保持对象,表示对象在引用计数上被强保留。
  • weakweak 引用不会增加引用计数,因此不会导致对象无法释放。在对象被销毁时,weak 引用会自动设置为 nil,避免悬挂指针的问题。

使用例子:

@interface MyClass : NSObject
@property (nonatomic, strong) MyClass *strongObject;  // 强引用
@property (nonatomic, weak) MyClass *weakObject;      // 弱引用
@end
  • 强引用 (strong):确保对象在引用它的地方存在,直到没有任何强引用指向它。
  • 弱引用 (weak):不保持对象的生命周期,如果对象被释放,它的引用会自动变为 nil,防止访问已释放对象导致崩溃。

以 “自动释放池块” 降低内存峰值

当我们在调用autorelease的时候,我们就会将这个对象加入自动释放池,创建自动释放池的方式如下

@autoreleasepool {}

如果在没有创建自动释放池的情况下,直接使用autorelease的话,编译器就会弹出以下警告

Object Oxabcd0123 of class NSCFString autoreleased with no pool ni place - just leaking - break on objc_autoreleaseNoPool()to debug

一般来说,我们并不会担心自动释放池的问题,因为在iOS程序的main函数当中经常这么写:

int main(int argc, char * argv[]) {NSString * appDelegateClassName;@autoreleasepool {// Setup code that might create autoreleased objects goes here.appDelegateClassName = NSStringFromClass([AppDelegate class]);}return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

花括号定义了自动释放池的边界,从左花括号开始创建,到右花括号就自动清空,自动释放池也可以嵌套,书中的例子:

@autoreleasepool {// 创建一个字符串,格式化输出数字 1NSString *string = [NSString stringWithFormat:@"1 = %d", 1];NSLog(@"%@", string);@autoreleasepool {// 在内层自动释放池中创建一个 NSNumber 对象NSNumber *number = [NSNumber numberWithInt:1];NSLog(@"%@", number);}
}

将自动释放池嵌套用的好处是,可以借此控制应用程序的内存峰值,使其不致过高。那么什么是内存峰值呢?我们再举一个例子

NSArray *databaseRecords - */ ... */ ; 
NSMutableArray *people = [NSMutableArray new];
for (NSDictionary *record in databaseRecords) {EOCPerson *person = [[EOCPerson alloc] initWithRecord: record];[people addobject: person] ;
}

假设我们要从一个数据库读取很多数据,不难看出就会创建出大量person的临时对象,那么势必占据许多内存,如果我们将自动释放池放在循环外面,自动释放池只会在循环结束释放内存,而我们可以添加自动释放池添加在循环之中。

NSArray *databaseRecords - */ ... */ ; 
NSMutableArray *people = [NSMutableArray new];
for (NSDictionary *record in databaseRecords) {@autoreleasepool{EOCPerson *person = [[EOCPerson alloc] initWithRecord: record];[people addobject: person] ;}}

这个程序就会在循环运行的过程之中对临时对象进行释放,应用程序执行循环的内存峰值就会降低。内存峰值(high-memory waterline)是指应用程序在某个特定时段内的最大内存用量 (highest memory footprint )。自动释放机制就像栈一样,系统创建好池以后,就将其自动推入池中,相当于从栈上弹出。在对象执行自动释放操作就等同于推入池中。

用 “僵尸对象” 调试内存管理问题

正常释放 vs. 僵尸对象
在正常情况下,当一个对象的引用计数降为 0 时,系统会调用它的 dealloc 方法,从内存中真正销毁该对象。然而,如果在对象被销毁后继续发送消息,就会发生“野指针”访问,导致程序崩溃(通常表现为 EXC_BAD_ACCESS)。

僵尸对象的机制
当启用僵尸对象调试后,对象在引用计数为 0 时不会真正被销毁,而是转变为“僵尸”(zombie)状态。僵尸对象会保留在内存中,但会拦截所有后续发送给它的消息,并打印出一条错误日志,指出你正在尝试对一个已经释放的对象进行操作。这样,你就能准确定位是哪种对象在被错误地访问,从而帮助你查找内存管理错误。

启用这项调试功能之后,运行期系统会把所有已经回收的实例转化成特殊的“僵尸对象”,而不会真正回收 它们。这种对象所在的核心内存无法重用 ,因此不可能遭到覆写。僵尸对象收到消息后,会抛出异常,其中准确说明了发送过来的消息,并描述了回收之前的那个对象。

如果程序在发送消息时崩溃(如 EXC_BAD_ACCESS),启用僵尸对象后,Xcode 的调试输出会提示类似下面的错误信息:

*** -[SomeClass someMethod]: message sent to deallocated instance 0x12345678

书中用了一个例子:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>@interface EOCClass : NSObject
@end@implementation EOCClass
@endvoid PrintClassInfo(id obj) {Class cls = object_getClass(obj);Class superCls = class_getSuperclass(cls);NSLog(@"Class: %@, Superclass: %@", class_getName(cls), class_getName(superCls));
}int main(int argc, char *argv[]) {EOCClass *obj = [[EOCClass alloc] init];NSLog(@"Before release:");PrintClassInfo(obj); // 输出当前对象的类名[obj release]; // 释放对象NSLog(@"After release:");PrintClassInfo(obj); // 释放后的对象已经变为僵尸对象// 如果开启了僵尸对象功能,发送消息会打印以下内容NSString *desc = [obj description]; // 发送消息到已释放对象return 0;
}

我们运行这段程序我们得到以下结果

Before release:
=== EOCClass :NSOb ject === 
After release:
== _NSZombie_EoCClass:nil ==

我们看到对象所属的类从 EOCClass 变 为 _NSZombie_EOCClass ,这个类是在编译过程之中形成的,当首次碰到EOCClass 类的对象要变成僵尸对象时,就会创建这么一个类。创建过程中用到了运行期程序库里的函数,它们的功能很强大 ,可以操作类列表(classlist)。以下是创造僵尸对象的代码方法

#import <objc/runtime.h>void handleZombieObject(id self) {// 获取对象的类Class cls = object_getClass(self);// 获取类的名字const char *clsName = class_getName(cls);// 将 "_NSZombie_" 前缀加到类名上const char *zombieClsName = [NSString stringWithFormat:@"_NSZombie_%s", clsName].UTF8String;// 查找特定的僵尸类Class zombieCls = objc_lookUpClass(zombieClsName);// 如果这个特定的僵尸类不存在,创建它if (!zombieCls) {// 获取模板僵尸类 _NSZombie_Class baseZombieCls = objc_lookUpClass("_NSZombie_");// 复制模板僵尸类,创建新的僵尸类,名字加上原类名的前缀zombieCls = objc_duplicateClass(baseZombieCls, zombieClsName, 0);}// 执行对象的正常销毁过程objc_destructInstance(self);// 将对象的类更改为僵尸类objc_setClass(self, zombieCls);// 现在对象的类已经变成 _NSZombie_OriginalClass// 这样即便对象被释放后,仍能捕捉到发送给它的消息
}

僵尸类的作用会在消息转发例程中体现出来 ,此类没有超类,因此和NSObject 一样,也是个 “根类”,该类只有 一个实例变量,叫做isa。由于这个僵尸类没有实现任何方法,那么对于编译器来说,只会实现之前学习过的消息转发之中的完整的消息转发,在forward方法之中,若名称前缀为_NSZombie_,则表明消息接收者是僵尸对象,需要特殊处理 。此时会打印一条消息,其中指明了僵尸对象所收到的消息及原来所属的类,然后应用程序就 终止了。在僵尸类名中嵌入原始类名的好处,这时就可以看出来了。只要把_NSZombie_从僵尸类名的开头拿掉,剩下的就是原始类名。下列伪代码演示了这一过程:

// 获取对象的类
Class cls = object_getClass(self);// 获取类的名字
const char *clsName = class_getName(cls);// 检查类名是否以 "_NSZombie_" 开头
if (string_has_prefix(clsName, "_NSZombie_")) {// 如果是,则说明该对象是僵尸对象// 取出原始类名,即跳过 "_NSZombie_" 前缀(前10个字符)const char *originalClsName = substring_from(clsName, 10);// 获取当前正在发送的消息的选择子名称const char *selectorName = sel_getName(_cmd);// 输出日志,指出哪个选择子被发送到了哪个僵尸对象上Log("*** -[%s %s]: message sent to deallocated instance %p",originalClsName, selectorName, self);// 终止程序运行abort();
}

不要使用retainCount

  1. 内部实现的不确定性
    retainCount 返回的是对象当前的引用计数,但这个值可能受到系统内部实现细节的影响。例如,自动释放池(autoreleasepool)和其他运行时机制可能会临时增加或减少引用计数,从而使返回的值与预期不符。
  2. 不可靠的结果
    由于系统在幕后对对象的引用计数进行优化处理,retainCount 并不能准确反映实际的引用关系。你可能会看到一个值很高或很低,但这并不意味着对象马上会被销毁或保持长期存在。
  3. 不便于调试和维护
    依赖 retainCount 的检查往往会导致代码变得脆弱,因为引用计数的变化是由许多因素决定的。使用这种方法很难编写出健壮、可维护的代码。开发者应该关注对象的拥有关系(ownership),而不是直接依赖数值。
  4. 正确的内存管理方式
    在 ARC(自动引用计数)时代,内存管理已经由编译器自动处理,开发者无需关心具体的引用计数。即使在 MRC 环境下,也应遵循正确的内存管理规则(如遵循所有权原则、及时释放对象),而不是依赖 retainCount

总之,retainCount 只是一个调试时参考的工具,并不能作为判断对象生命周期或内存管理正确性的依据。在实际开发中,我们应通过正确的内存管理模式(例如 ARC、遵循内存管理规则)来确保代码的健壮性,而不依赖 retainCount 的返回值。

相关文章:

Effective Objective-C 2.0 读书笔记——内存管理(下)

Effective Objective-C 2.0 读书笔记——内存管理&#xff08;下&#xff09; 在 dealloc 方法中只释放引用并解除监听 对象在经历其生命期后 &#xff0c;最终会为系统所回收 &#xff0c;这时就要执行dealloc 方法了。 在每个对象的生命期内&#xff0c;此方法仅执行一次&a…...

[Spring Boot] Expense API 实现

[Spring Boot] Expense API 实现 项目地址&#xff1a;expense-api 项目简介 最近跟着视频做的一个 spring boot 的项目&#xff0c;包含了比较简单的记账功能的实现&#xff08;只限 API 部分&#xff09;&#xff0c;具体实现的功能有&#xff1a; 记账&#xff08;expen…...

Pell数列【一本通在线评测】

目录 描述 输入描述 输出描述 用例输入 1 用例输出 1 什么是pell数列 一、定义与递推关系 二、数学性质 三、应用领域 四、编程实现要点 五、扩展与相关概念 C代码实现 描述 Pell数列a1​,a2​,a3​,...的定义是这样的&#xff0c;a1​1,a2​2,...,an​2an−1​a…...

linux ollama deepseek等大语言模型的model文件的存储目录

linux ollama deepseek等大语言模型的model文件的存储目录 一、用ollama serve启动的&#xff0c;模型数据存放在&#xff1a; /usr/share/ollama/.ollama/models二、如果在自启动文件中指定了工作目录&#xff0c;则在工作目录下的.ollama/models 1.自启动服务 /etc/system…...

【PyQt】工具栏(QToolBar)与动作按钮(QAction)使用指南

PyQt工具栏(QToolBar)与动作按钮(QAction)使用指南 &#x1f6e0;️ 一、基础用法示例 &#x1f31f; class MainWindow(QMainWindow):def __init__(self):super().__init__()# 创建工具栏 &#x1f527;self.toolbar self.addToolBar("主工具栏")# 创建动作集合 …...

软路由折腾 | OpenWrt安装后基础配置指南:联网设置与DNS优化

在PVE中安装OpenWrt教程 一、网络基础配置 1. 确认网络接口角色 OpenWrt旁路由通常仅需配置LAN口&#xff0c;无需WAN口。其流量通过主路由转发&#xff0c;因此需确保&#xff1a; 物理连接&#xff1a;OpenWrt的LAN口&#xff08;如eth0&#xff09;桥接到主路由的局域网&…...

设置默认构建变体 Build Variant

Android Studio在打开项目时有时会把我设置好的build Variant改为默认的变体&#xff0c;没注意的话可能打完包才发现打错了&#xff0c;浪费时间。因此&#xff0c;有必要通过代码设置一个我想要的默认变体。 代码其实很简单&#xff0c;只要在变体下面加上isDefault true即可…...

【大模型】DeepSeek使用与原理解析:从V3到R1

文章目录 一、引言二、使用与测评1.7大R1使用技巧2.官网实测 发展历程三、Deepseek MoE&#xff1a;专家负载均衡 &#xff08;2024年1月&#xff09;四、GRPO&#xff1a;群体相对策略优化&#xff08;DeepSeek-Math&#xff0c;2024年4月&#xff09;五、三代注意力&#xff…...

DAY04 Object、Date类、DateFormat类、Calendar类、Math类、System类

学习目标 能够说出Object类的特点是所有类的祖宗类,任意的一个类都直接或者间接的继承了Object类,都可以使用Object类中的方法Animal extends Object:直接继承Cat extends Animal:间接继承 能够重写Object类的toString方法altinsert,选择toString 能够重写Object类的equals方法…...

oracle 19c安装DBRU补丁时报错CheckSystemSpace的处理

oracle 19c的补丁目前已经发布到19.26版本了&#xff0c;数据库补丁安装也是数据库运维中的一个常见工作&#xff1b;近期在一个安装补丁的环境遇到了Prerequisite check "CheckSystemSpace" failed.错误&#xff0c;看起来是磁盘剩余空间不足的告警&#xff1b;按以…...

图像生成GAN和风格迁移

文章目录 摘要abstract1.生成对抗网络 GAN1.1 算法步骤 2.风格迁移2.1 损失函数2.2 论文阅读2.2.1 简介2.2.2 方法2.2.3 实验2.2.4 结论 3.总结 摘要 本周学习了生成对抗网络&#xff08;GAN&#xff09;与风格迁移技术在图像生成中的应用。首先介绍了GAN模型中生成器与判别器…...

golangAPI调用deepseek

目录 1.deepseek官方API调用文档1.访问格式2.curl组装 2.go代码1. config 配置2.模型相关3.错误处理4.deepseekAPI接口实现5. 调用使用 3.响应实例 1.deepseek官方API调用文档 1.访问格式 现在我们来解析这个curl 2.curl组装 // 这是请求头要加的参数-H "Content-Type:…...

【第15章:量子深度学习与未来趋势—15.3 量子深度学习在图像处理、自然语言处理等领域的应用潜力分析】

一、开篇:为什么我们需要关注这场"量子+AI"的世纪联姻? 各位技术爱好者们,今天我们要聊的这个话题,可能是未来十年最值得押注的技术革命——量子深度学习。这不是简单的"1+1=2"的物理叠加,而是一场可能彻底改写AI发展轨迹的范式转移。 想象这样一个…...

JAVA安全—Shiro反序列化DNS利用链CC利用链AES动态调试

前言 讲了FastJson反序列化的原理和利用链&#xff0c;今天讲一下Shiro的反序列化利用&#xff0c;这个也是目前比较热门的。 原生态反序列化 我们先来复习一下原生态的反序列化&#xff0c;之前也是讲过的&#xff0c;打开我们写过的serialization_demo。代码也很简单&…...

LangChain大模型应用开发:提示词工程应用与实践

介绍 大家好&#xff0c;博主又来给大家分享知识了。今天给大家分享的内容是LangChain提示词工程应用与实践。 在如今火热的大语言模型应用领域里&#xff0c;LangChain可是一个相当强大且实用的工具。而其中的提示词(Prompt)&#xff0c;更是我们与语言模型进行有效沟通的关…...

【数据结构入门 65 题】目录

目录 1 函数题2 编程题3 数据结构实现 1 函数题 6-1 单链表逆转 6-2~6-6 线性表基本操作 6-7 在一个数组中实现两个堆栈 6-8 求二叉树高度 6-9 二叉树的遍历 6-10 二分查找 6-11 先序输出叶结点 6-12 二叉搜索树的操作集 2 编程题 3 数据结构实现 栈和队列...

osgearth控件显示中文(八)

当前自己知道的方法大概有以下两种: (一)直接转成utf8 其实在前面的文章中已经有了。 osgEarth::Annotation::PlaceNode *pn = new osgEarth::Annotation::PlaceNode(GeoPoint(geoSRS, 110, 34), String2UTF8("中国"), style);std::wstring String2Wstring(con…...

2025 N1CTF crypto 复现

近一个月都没有学习了&#xff0c;一些比赛也没有打&#xff0c;很惭愧自己还是处在刚放假时的水平啊&#xff0c;马上开学了&#xff0c;抓紧做一些训练来康复。 CheckIn import os from Crypto.Util.number import * from secret import FLAGp, q getPrime(512), getPrime…...

Windows Defender Control--禁用Windows安全中心

Windows Defender Control--禁用Windows安全中心 链接&#xff1a;https://pan.xunlei.com/s/VOJDuy2ZEqswU4sEgf12JthZA1?pwdtre6#...

mount 出现 2038 问题

在 linux 上挂载 ext4 文件系统时出现了 2038 年问题&#xff0c;如下&#xff1a; [ 236.388500] EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. Opts: (null) [ 236.388560] ext4 filesystem being mounted at /root/tmp supports timestamps until 2…...

langchain+不同模型api使用教程

目录 一、Langchainopenai1. gpt-4o2. o13. o3-mini 二、Anthropic1.claude-3-5-sonnet-latest 三、四、 一、Langchainopenai langchain_community 可能是一个较旧的库&#xff0c;或者它的版本与 OpenAI 的最新 API 不兼容。OpenAI 的 API 可能会更新&#xff0c;而 langcha…...

【第12章:深度学习与伦理、隐私—12.4 深度学习与伦理、隐私领域的未来挑战与应对策略】

凌晨三点的自动驾驶测试场,AI系统突然在暴雨中做出惊人决策——它选择撞向隔离带而不是紧急变道,因为算法推演发现隔离带后的应急车道站着五个工程师。这个惊悚的伦理困境,揭开了深度学习伦理危机最尖锐的冰山一角。 一、潘多拉魔盒已开:深度学习伦理的四大原罪 1.1 数据原…...

2. 图片性能优化

图片性能优化 图片懒加载 如何判断图片出现在了当前视口 &#xff08;即如何判断我们能够看到图片&#xff09;如何控制图片的加载 原生实现 <img src"shanyue.jpg" loading"lazy" />loading"lazy" 延迟加载图像&#xff0c;直到它和视…...

Java与DeepSeek的完美结合:开启高效智能编程新时代 [特殊字符]

一、DeepSeek&#xff1a;Java开发者的智能编程伙伴 &#x1f916; 1.1 DeepSeek是什么&#xff1f; DeepSeek是一款AI驱动的智能编程工具&#xff0c;通过深度学习和自然语言处理技术&#xff0c;为Java开发者提供&#xff1a; 智能代码补全&#xff1a;根据上下文预测代码 …...

RL--2

强化学习当中最难的两个点是&#xff1a; 1.reward delay&#xff1b; 2.agent的行为会影响到之后看到的东西&#xff0c;所以agent要学会探索世界&#xff1b; 关于强化学习的不同类型&#xff0c;可以分为以下三种&#xff1a; 一种是policy based&#xff1a;可以理解为它是…...

SpringMVC新版本踩坑[已解决]

问题&#xff1a; 在使用最新版本springMVC做项目部署时&#xff0c;浏览器反复500&#xff0c;如下图&#xff1a; 异常描述&#xff1a; 类型异常报告 消息Request processing failed: java.lang.IllegalArgumentException: Name for argument of type [int] not specifie…...

2025 pwn_A_childs_dream

文章目录 fc/sfc mesen下载和使用推荐 fc/sfc https://www.mesen.ca/docs/ mesen2安装&#xff0c;vscode安装zg 任天堂yyds w d 左右移动 u结束游戏 i崩溃或者卡死了 L暂停 D658地方有个flag 发现DEEE会使用他。且只有这个地方&#xff0c;maybe会输出flag&#xff0c;应…...

pandas(11 分类数据和数据可视化)

前面内容&#xff1a;pandas(10 日期和Timedelta) 目录 一、Python Pandas 分类数据 1.1 pd.Categorical() 1.2 describe() 1.3 获取类别的属性 1.4 分类操作 1.5 分类数据的比较 二、Python Pandas 数据可视化 2.1 基础绘图&#xff1a;plot 2.2 条形图 2.3 直方…...

Redis 03章——10大数据类型概述

一、which10 &#xff08;1&#xff09;一图 &#xff08;2&#xff09;提前声明 这里说的数据类型是value的数据类型&#xff0c;key的类型都是字符串 官网&#xff1a;Understand Redis data types | Docs &#xff08;3&#xff09;分别是 1.3.1redis字符串&#xff0…...

bps是什么意思

本文来自DeepSeek "bps" 是 "bits per second" 的缩写&#xff0c;表示每秒传输的比特数&#xff0c;用于衡量数据传输速率。1 bps 即每秒传输 1 比特。 常见单位 bps&#xff1a;比特每秒 Kbps&#xff1a;千比特每秒&#xff08;1 Kbps 1,000 bps&am…...