【iOS】源码阅读(五)——类类的结构分析
文章目录
- 前言
- 类的分析
- 类的本质
- objc_class 、objc_object和NSObject
- objc_object:所有对象的基类型
- objc_class:类的底层结构
- NSObject:面向用户的根类
- 小结
- 指针内存偏移
- 普通指针----值拷贝
- 对象----指针拷贝或引用拷贝
- 用数组指针引出----内存偏移
- 类的结构
- Class ISA
- Class surperclass
- cache_t cache
- class_data_bits_t bits
- 总结
前言
本篇博客主要是笔者在学习类&类的结构的底层探索和分析时所作的笔记,主要涉及实例对象的类以及类的结构。Objective-C的类结构是其动态性和面向对象特性的核心,理解类的内存布局和内部机制,对开发、调试和性能优化至关重要。
类的分析
类的本质
在 OC 中,类(Class)本身是一个对象(objc_class 结构体),在探索类的本质之前,我们先在main文件里自定义一个继承自NSobjetc的TCJPerson类:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>@interface TCJPerson : NSObject@end@implementation TCJPerson@endint main(int argc, const char * argv[]) {@autoreleasepool {TCJPerson *person = [TCJPerson alloc];Class cls = object_getClass(person);}return 0;
}
然后利用clang工具将oc语言的main文件输出为cpp文件,命令行如下:
clang -rewrite-objc main.m -o main.cpp
将文件拖到我们的objc源码中打开,方便我们调试和查找,可以发现,这段代码在cpp文件文件中如下:
//类型定义部分
#ifndef _REWRITER_typedef_TCJPerson
#define _REWRITER_typedef_TCJPerson
typedef struct objc_object TCJPerson; //将 TCJPerson 定义为 objc_object 结构体
typedef struct {} _objc_exc_TCJPerson; //空结构体,用于异常处理占位(实际未使用)
#endif//类的实现结构
struct TCJPerson_IMPL {struct NSObject_IMPL NSObject_IVARS; //继承自 NSObject 的实例变量
};/* @end */// @implementation TCJPerson
// @end
//main函数的底层转换
int main(int argc, const char * argv[]) {/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; TCJPerson *person = ((TCJPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("TCJPerson"), sel_registerName("alloc"));//objc_getClass("TCJPerson"):获取 TCJPerson 的类对象(Class)//sel_registerName("alloc"):注册方法名 alloc,返回 SEL 类型的选择子//objc_msgSend:发送 alloc 消息给 TCJPerson 类,创建实例//强制类型转换 (TCJPerson *(*)(id, SEL))(void *) 是为了匹配 objc_msgSend 的函数指针签名Class cls = object_getClass(person);//获取 person 实例的类(即 TCJPerson 的类对象)}return 0;
}
- TCJPerson 被定义为 objc_object,说明在底层,Objective-C 类实例的本质就是 objc_object 结构体。
- 因为 TCJPerson 继承自 NSObject,所以它的底层结构会包含父类的实例变量。其中,NSObject_IVARS 就是 NSObject 的实例变量,通常就是 isa 指针(指向类的元数据)。
然后我们发现类在底层是用class接收的。
typedef struct objc_class *Class;
在左侧搜索栏查找objc_class,我们可以发现objc_class继承自objc_object。
点击进入objc_object的源码实现中:
小结
- Objective-C 对象的本质:
类实例(如 person)本质是 objc_object 结构体,包含 isa 指针(来自 NSObject_IVARS)。类(如 TCJPerson)本质是 objc_class 结构体,继承自 objc_object。所以满足万物皆对象。 - 方法调用的本质:[TCJPerson alloc] 被编译为 objc_msgSend 的调用,动态查找并执行方法。
- 内存布局:TCJPerson_IMPL 只包含 NSObject 的 isa,因为没有自定义实例变量。
objc_class 、objc_object和NSObject
objc_object:所有对象的基类型
底层源码:
struct objc_object {Class _Nonnull isa OBJC_ISA_AVAILABILITY; //必须为非空的类指针(即指向 objc_class 结构体的指针)
};
这证明objc_object是所有 Objective-C 对象的最底层结构,包括实例对象、类对象、元类对象。其通过 isa 指针实现对象与类的关联(即“对象是什么类”)。
特点:
实例对象的 isa 指向它的类(如 TCJPerson 实例的 isa 指向 TCJPerson 类)。
类对象的 isa 指向元类(Meta Class)。
元类的 isa 指向根元类(Root Meta Class)。
关于类 元类 根元类
- 类(Class)
作用:定义对象的实例变量和实例方法(如 -init、-description)。
内存结构:每个类是一个 objc_class 结构体实例,包含方法列表、父类指针、缓存等。
对象关系:实例对象(如 MyObject *obj)的 isa 指针指向其类对象。 - 元类(Meta-class)
作用:定义类的类方法(如 +alloc、+new)。
元类本身也是一个类,它的实例是类对象。
内存结构:元类也是一个 objc_class 结构体,但其方法列表存储类方法。
对象关系:类对象(如 MyObject.class)的 isa 指针指向其元类。 - 根元类(Root Meta-class)
作用:所有元类的最终基类,通常是 NSObject 的元类。
根元类的类方法(如 +alloc)会被所有类的元类继承。
根元类的 isa 指针指向自身,形成闭环。
Instance (MyObject): 一个对象实例
Class (MyObject class): 定义该实例的类
Meta-class (MyObject meta): 定义该类的元类
isa 指针:形成继承链的核心,每个实例、类和元类都有一个指向其类型的指针
实例通过 isa 指向类,类通过 isa 指向元类,元类通过 isa 指向根元类
super_class 指针:形成继承体系
类通过 super_class 指向父类,元类通过 super_class 指向父元类
Root Meta-class (NSObject meta): 所有元类的根元类
根元类的 isa 指向自己(self),形成闭环
根元类的 super_class 指向 NSObject 类
运行时方法查找路径:
实例方法首先在实例所属的类中查找
如果没找到,则沿着 super_class 链向上查找
类方法则在元类及其父元类中查找
继承机制:实例继承自类,类继承自元类,元类继承自父元类,最终根元类继承自NSObject类
objc_class:类的底层结构
底层代码:
struct objc_class : objc_object { //继承自objc_objectobjc_class(const objc_class&) = delete;objc_class(objc_class&&) = delete;void operator=(const objc_class&) = delete;void operator=(objc_class&&) = delete;// Class ISA;Class superclass; //父类指针cache_t cache; // formerly cache pointer and vtable //方法缓存class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags //类的方法、属性、协议等数据Class getSuperclass() const {
#if __has_feature(ptrauth_calls)
# if ISA_SIGNING_AUTH_MODE == ISA_SIGNING_AUTHif (superclass == Nil)return Nil;......
}
这说明objc_class是 objc_object 的子类,说明类本身也是对象(即“类对象”)。
存储类的元数据:方法列表、属性列表、协议列表、父类指针等。
因为其继承自 objc_object,所以其类对象也有 isa 指针(指向元类)。
其中,Class 是指向 objc_class 的指针(typedef struct objc_class *Class)。
NSObject:面向用户的根类
底层代码:
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"Class isa OBJC_ISA_AVAILABILITY; //指向类对象的指针
#pragma clang diagnostic pop
}
NSObject 是 Objective-C 中所有类的根类(除了 NSProxy),提供面向开发者的基础方法(如 alloc、init、description)。
与 objc_object 的关系:
NSObject 的实例对象在底层就是 objc_object。
NSObject 的类对象在底层是 objc_class。
编译器会将 NSObject 的代码转换为对 objc_object 和 objc_class 的操作。
小结
通过对objc_class、objc_object和NSObject底层的大致了解,我们就可以明白三者存在以下金字塔关系:
objc_object:所有对象的终极基类(C结构体)
objc_class:继承objc_object,说明"类也是对象"
NSObject:继承链最顶端的公开类,封装了objc_class的面向对象接口
小结
所有的对象 + 类 + 元类 都有isa属性
所有的对象都是由objc_object继承来的
简单概括就是万物皆对象,万物皆来源于objc_object,有以下两点结论:
所有以 objc_object为模板创建的对象,都有isa属性
所有以 objc_class为模板创建的类,都有isa属性
在结构层面可以通俗的理解为上层OC 与 底层的对接:
下层是通过 结构体 定义的 模板,例如objc_class、objc_object
上层是通过底层的模板创建的一些类型,例如TCJPerson
objc_class、objc_object、isa、object、NSObject等的整体的关系如下图:
指针内存偏移
在分析类的结构之前,我们需要先来学习一下指针内存偏移作为前缀知识。
普通指针----值拷贝
//普通指针//值拷贝int a = 10;int b = 10;NSLog(@"&a:%d--%p", a, &a);NSLog(@"&b:%d--%p", b, &b);
通过代码及其运行结果可以看出来,变量a和b虽然都是被赋值为10,但是变量a和b的内存地址是不一样的,我们称这种方式为值拷贝。
对象----指针拷贝或引用拷贝
//对象NSObject *obj1 = [[NSObject alloc] init];NSObject *obj2 = [[NSObject alloc] init];NSLog(@"%@--%p", obj1, &obj1);NSLog(@"%@--%p", obj2, &obj2);
通过运行结果,我们可以看到obj1和obj2对象不仅自身内存地址不一样,其指向的对象的内存地址也不一样,这被称为指针拷贝或引用拷贝。
用数组指针引出----内存偏移
//数组指针int arr[4] = {1, 2, 3, 4};int *c = arr;NSLog(@"&arr:%p--%p--%p", arr, &arr[0], &arr[1]);NSLog(@"&c:%p--%p--%p", c, c + 1, c + 2);for (int i = 0; i < 4; i++) {int value = *(c + i);NSLog(@"value:%d", value);}
通过运行结果可以看到:
- &a和&a[0]的地址是相同的——即首地址就代表数组的第一个元素的地址。
- 第一个元素地址0x16fdff2f8和第二个元素地址0x16fdff2fc相差4个字节,也就是int的所占的4字节,因为他们的数据类型相同。
- d、d+1、d+2这个地方的指针相加就是偏移地址。地址加1就是偏移,偏移一个位数所在元素的大小。
- 可以通过地址,取出对应地址的值。
小结
类的结构
从objc_class的定义可以得出,类有4个属性:isa、superclass、cache、bits。
Class ISA
不但实例对象中有isa指针,类对象中也有isa指针关联着元类。
Class本身就是一个指针,占用8字节。
Class surperclass
顾名思义就是类的父类(一般为NSObject)superclass是Class类型,所以占用8字节。
cache_t cache
进入cache_t的实现源码,我们能看到(笔者对部分进行了注释,方便理解):
struct cache_t {
private:explicit_atomic<uintptr_t> _bucketsAndMaybeMask; //原子存储缓存桶指针或掩码(根据配置不同复用同一内存)//原子性:保证并发访问时的线程安全(如 objc_msgSend 高频调用场景)union {// Note: _flags on ARM64 needs to line up with the unused bits of// _originalPreoptCache because we access some flags (specifically// FAST_CACHE_HAS_DEFAULT_CORE and FAST_CACHE_HAS_DEFAULT_AWZ) on// unrealized classes with the assumption that they will start out// as 0.struct {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED && !__LP64__// Outlined cache mask storage, 32-bit, we have mask and occupied.explicit_atomic<mask_t> _mask; //缓存掩码uint16_t _occupied; //已占用槽位数
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED && __LP64__// Outlined cache mask storage, 64-bit, we have mask, occupied, flags.explicit_atomic<mask_t> _mask;uint16_t _occupied;uint16_t _flags; //状态标志(FAST_CACHE_HAS_DEFAULT_CORE: 是否有默认核心实现 FAST_CACHE_HAS_CUSTOM_DEALLOC_INITIATION: 是否自定义释放逻辑)
# define CACHE_T_HAS_FLAGS 1
#elif __LP64__// Inline cache mask storage, 64-bit, we have occupied, flags, and// empty space to line up flags with originalPreoptCache.//// Note: the assembly code for objc_release_xN knows about the// location of _flags and the// FAST_CACHE_HAS_CUSTOM_DEALLOC_INITIATION flag within. Any changes// must be applied there as well.uint32_t _unused;uint16_t _occupied;uint16_t _flags;
# define CACHE_T_HAS_FLAGS 1
#else// Inline cache mask storage, 32-bit, we have occupied, flags.uint16_t _occupied;uint16_t _flags;
# define CACHE_T_HAS_FLAGS 1
#endif
cache在英文中的意思是缓存。
cache_t是一个结构体,内存长度由所有元素决定:
_bucketsAndMaybeMask是long类型,它是一个指针,占用8字节;
mask_t是个uint32_t类型,_mask占用4字节;
_occupied和_flags都是uint16_t类型,uint16_t是 unsigned short 的别名,所以_occupied占用2字节;
_flags占用2字节;
所以,cache_t共占用16字节。
这里对cache简单了解一下,后面还会呢详细学习。
class_data_bits_t bits
class_data_bits_t实现源码如下:
struct class_data_bits_t {friend objc_class;// Values are the FAST_ flags above.uintptr_t bits;
private:bool getBit(uintptr_t bit) const{return bits & bit;}// Atomically set the bits in `set` and clear the bits in `clear`.// set and clear must not overlap. If the existing bits field is zero,// this function will mark it as using the RW signing scheme.void setAndClearBits(uintptr_t set, uintptr_t clear){ASSERT((set & clear) == 0);uintptr_t newBits, oldBits = LoadExclusive(&bits);do {uintptr_t authBits= (oldBits? (uintptr_t)ptrauth_auth_data((class_rw_t *)oldBits,CLASS_DATA_BITS_RW_SIGNING_KEY,ptrauth_blend_discriminator(&bits,CLASS_DATA_BITS_RW_DISCRIMINATOR)): FAST_IS_RW_POINTER);newBits = (authBits | set) & ~clear;newBits = (uintptr_t)ptrauth_sign_unauthenticated((class_rw_t *)newBits,CLASS_DATA_BITS_RW_SIGNING_KEY,ptrauth_blend_discriminator(&bits,CLASS_DATA_BITS_RW_DISCRIMINATOR));} while (slowpath(!StoreReleaseExclusive(&bits, &oldBits, newBits)));}void setBits(uintptr_t set) {setAndClearBits(set, 0);}void clearBits(uintptr_t clear) {setAndClearBits(0, clear);}public:void copyRWFrom(const class_data_bits_t &other) {bits = (uintptr_t)ptrauth_auth_and_resign((class_rw_t *)other.bits,CLASS_DATA_BITS_RW_SIGNING_KEY,ptrauth_blend_discriminator(&other.bits,CLASS_DATA_BITS_RW_DISCRIMINATOR),CLASS_DATA_BITS_RW_SIGNING_KEY,ptrauth_blend_discriminator(&bits,CLASS_DATA_BITS_RW_DISCRIMINATOR));}void copyROFrom(const class_data_bits_t &other, bool authenticate) {ASSERT((flags() & RO_REALIZED) == 0);if (authenticate) {bits = (uintptr_t)ptrauth_auth_and_resign((class_ro_t *)other.bits,CLASS_DATA_BITS_RO_SIGNING_KEY,ptrauth_blend_discriminator(&other.bits,CLASS_DATA_BITS_RO_DISCRIMINATOR),CLASS_DATA_BITS_RO_SIGNING_KEY,ptrauth_blend_discriminator(&bits,CLASS_DATA_BITS_RO_DISCRIMINATOR));} else {bits = other.bits;}}
虽然我们不是很能看懂,但至少能看出来这里是oc运行时用来存储数据的。
根据上面的分析,class指针各为8字节,cache_t cache为16字节,所以想要获取bits的中的内容,只需通过类的首地址平移32字节即可。
总结
在学习过程中,笔者关于LLDB调试出了比较多的问题,至今还没解决,所以这篇笔记还有待完善,等笔者解决完问题后,会再次进行编撰,LLDB在源码学习中还是很不错的工具。
相关文章:

【iOS】源码阅读(五)——类类的结构分析
文章目录 前言类的分析类的本质objc_class 、objc_object和NSObjectobjc_object:所有对象的基类型objc_class:类的底层结构NSObject:面向用户的根类 小结 指针内存偏移普通指针----值拷贝对象----指针拷贝或引用拷贝用数组指针引出----内存偏…...

基于CangjieMagic的RAG技术赋能智能问答系统
目录 引言 示例程序分析 代码结构剖析 导入模块解读 智能体配置详情 提示词模板说明 主程序功能解析 异步聊天功能实现 检索信息展示 技术要点总结 ollama 本地部署nomic-embed-text 运行测试 结语 引言 这段时间一直在学习CangjieMagic。前几天完成了在CangjieMa…...

算力租赁革命:弹性模式如何重构数字时代的创新门槛
一、算力革命:第四次工业革命的核心驱动力 在科技飞速发展的当下,我们正悄然迎来第四次工业革命。华为创始人任正非在一场程序设计竞赛中曾深刻指出,这场革命的基础便是大算力。随着 5G、人工智能、大数据、物联网等信息技术的迅猛发展&am…...

图论回溯
图论 200.岛屿数量DFS 给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此外ÿ…...
使用arthas热替换在线运行的java class文件
如果我们在线的系统有问题,但又无法停机进行发版或者仅仅改了一个java文件需要验证一下功能是否正常,这时可以使用arthas的在线热替换功能来做class文件的在线变更。 1.运行java -jar arthas-boot.jar,启动arathas,并选择正在运行的java的进…...

RFID测温芯片助力新能源产业安全与能效提升
在“双碳”目标驱动下,新能源产业正经历爆发式增长。无论是电动汽车、储能电站还是风光发电场,设备安全与能效提升始终是行业核心命题。而温度,这个看似普通的物理参数,却成为破解这一命题的关键密码。RFID测温芯片(集…...

S32K3 工具篇9:如何在无源码情况下灵活调试elf文件
S32K3 工具篇9:如何在无源码情况下灵活调试elf文件 一,文档简介二, 功能实现2.1 代码工具准备2.2 elf修改功能实现:Fun2功能跳过2.2.1 PC越过Fun22.2.2 Fun2替换为nop 2.3 elf修改功能实现:Fun4替换Fun2入口2.3.1 link…...

Nacos 配置文件总结
Nacos 配置文件总结 文章目录 Nacos 配置文件总结1 、在 Nacos 服务端添加配置文件1. 启动Nacos Server。2. 新建配置文件。3. 发布配置集后,我们便可以在配置列表中查看相应的配置文件。4. 配置nacos数据库5. 运行 Nacos 容器6. 验证安装结果7. 配置验证 2 、在 Na…...

ASP.NET Web Forms框架识别
ASP.NET 支持三种不同的开发模式: Web Pages(Web 页面)、MVC(Model View Controller 模型-视图-控制器)、Web Forms(Web 窗体): Web Pages 单页面模式MVC 模型-视图-控制器Web Form…...
LG P4119 [Ynoi2018] 未来日记 Solution
Description 给定序列 a ( a 1 , a 2 , ⋯ , a n ) a(a_1,a_2,\cdots,a_n) a(a1,a2,⋯,an),有 m m m 个操作分两种: replace ( l , r , x , y ) \operatorname{replace}(l,r,x,y) replace(l,r,x,y):将 a l ∼ a r a_l\sim a_r …...
流程引擎选型指南
流程引擎选型指南 流程引擎是企业实现业务流程自动化(BPM)的核心组件,选择合适的流程引擎对系统架构和未来发展至关重要。以下是主流流程引擎的综合对比和选型建议。 一、主流流程引擎对比 引擎名称开源/商业BPMN支持DMN支持CMMN支持云原生支持社区活跃度学习曲线…...
基于大模型预测带状疱疹(无并发症)诊疗方案的研究报告
目录 一、引言 1.1 研究背景与意义 1.2 研究目的与创新点 二、带状疱疹概述 2.1 病因与发病机制 2.2 流行病学特征 2.3 临床表现与诊断标准 三、大模型技术原理及应用于带状疱疹预测的可行性 3.1 大模型技术简介 3.2 应用可行性分析 四、大模型预测带状疱疹的具体方…...

哈工大计统大作业-程序人生
摘 要 本项目以“程序人生-Hellos P2P”为核心,通过编写、预处理、编译、汇编、链接及运行一个简单的Hello程序,系统探讨了计算机系统中程序从代码到进程的全生命周期。实验基于Ubuntu环境,使用GCC工具链完成代码转换,分析了预处…...

设计模式——装饰器设计模式(结构型)
摘要 文中主要介绍了装饰器设计模式,它是一种结构型设计模式,可在不改变原有类代码的情况下,动态为对象添加额外功能。文中详细阐述了装饰器模式的角色、结构、实现方式、适合场景以及实战示例等内容,还探讨了其与其他设计模式的…...

途景VR智拍APP:开启沉浸式VR拍摄体验
在数字化时代,VR技术以其沉浸式的体验逐渐走进了人们的日常生活。途景VR智拍APP作为一款集看图和拍照于一体的VR软件,为用户带来了全新的视觉体验和便捷的拍摄方式,无论是专业摄影师还是普通用户,都能轻松上手,拍出令人…...

Linux环境搭建MCU开发环境
操作系统版本: ubuntu 22.04 文本编辑器: vscode 开发板: stm32f103c8t6 调试器: st-link 前言 步骤一: 安装交叉编译工具链 步骤二: 创建工程目录结构 步骤三: 调试…...
Android高级开发第一篇 - JNI(初级入门篇)
文章目录 Android高级开发JNI开发第一篇(初级入门篇)🧠 一、什么是 JNI?✅ 为什么要用 JNI? ⚙️ 二、开发环境准备开发工具 🚀 三、创建一个支持 JNI 的 Android 项目第一步:创建新项目项目结构…...
Kubernetes RBAC权限控制:从入门到实战
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言:为什么需要RBAC? 在Kubernetes集群中,权限失控是导致安全漏洞的核心原因之一。试想以下场景: 开发…...
python实战项目71:基于Python的US News世界大学排名数据爬取
python实战项目71:基于Python的US News世界大学排名数据爬取 一、项目背景1.1 研究意义1.2 技术背景1.3 应用场景二、爬虫系统设计与实现2.1 分析页面、寻找数据真实接口2.2 发送请求,获取响应内容2.3 提取数据2.4 保存数据三、完整代码四、总结与展望一、项目背景 1.1 研究…...

【基础算法】高精度(加、减、乘、除)
文章目录 什么是高精度1. 高精度加法解题思路代码实现 2. 高精度减法解题思路代码实现 3. 高精度乘法解题思路代码实现 4. 高精度除法 (高精度 / 低精度)解题思路代码实现 什么是高精度 我们平时使用加减乘除的时候都是直接使用 - * / 这些符号,前提是进行运算的数…...
跨平台开发框架electron
桌面端开发框架有很多,比如C#的WPF和Winform,Dart的Flutter,JS的Electron,Rust的Tauri。 目前应用比较广的是Electron,比如我们常见的开发工具VsCode,就是基于Electron开发的。 所以这篇文章我们就来聊聊Electron。 简…...

Windows最快速打开各项系统设置大全
目录 一、应用背景 二、设置项打开方法 2.1 方法一界面查找(最慢) 2.2 方法二cmd命令(慢) 2.3 方法三快捷键(快) 2.4 方法四搜索栏(快) 2.5 方法五任务栏(最快&am…...

嵌入式编译工具链熟悉与游戏移植
在自己的虚拟机Ubuntu系统下,逐步编译 mininim源码(波斯王子重制开源版) 指令流程 sudo apt-get remove liballegro5-dev liballegro-image5-dev \liballegro-audio5-dev liballegro-acodec5-dev liballegro-dialog5-dev sudo apt-get install automak…...

DeepSeek-R1-0528,官方的端午节特别献礼
DeepSeek:端午安康!刻在国人骨子里的浪漫 2025 年 05 月 28 日 | DeepSeek 端午特别献礼 当粽叶飘香时,DeepSeek 悄然带来一份节日惊喜 版本号 DeepSeek-R1-0528 正式上线 官方赋予它的灵魂是: 思考更深 推理更强 用户通过官网…...
LNMP环境中php7.2升级到php7.4
以下是 CentOS 7 上从 PHP 7.2 升级到 PHP 7.4 的详细步骤,结合知识库中的方法和注意事项: 1.备份现有环境 #备份 PHP 配置文件 cp /etc/php.ini /etc/php.ini.bak cp -r /etc/php.d /etc/php.d.bak#备份网站文件和数据库 tar -czvf website_backup.tar…...

001 flutter学习的注意事项及前期准备
在学习flutter之前,还需要进行一些初始的配置,然后才可以学习flutter 1.安装flutter 国内官网:https://flutter.cn 国际官网:https://flutter.dev 安装完成后,按照官网上面的操作步骤进行配置…...
FactoryBean 接口
Spring 框架中 FactoryBean 接口的特性,这是 Spring 提供的一种特殊机制,用于创建和管理复杂 Bean。让我通过示例和解释帮您理解这个概念。 一、FactoryBean 是什么? FactoryBean 是 Spring 框架提供的一个工厂接口,用于创建复杂…...

CS144 - Lecture 1 记录
CS144 - Lecture 1 由于没讲义,全看课了,系统性的总结有点难,记一些有趣的东西吧。 数据链路和网络层的传输 我们可以看见,对于发送方,我们的数据链路层为我们的网络层提供服务,在经过路由的时候…...
【Redis】大key问题详解
目录 1、什么是大key2、大key的危害【1】阻塞风险【2】网络阻塞【3】内存不均【4】持久化问题 3、如何发现大key【1】使用内置命令【2】使用memory命令(Redis 4.0)【3】使用scan命令【4】监控工具 4、解决方案【1】拆分大key【2】使用合适的数据结构【3】…...

【数据结构】——二叉树--链式结构
一、实现链式结构二叉树 二叉树的链式结构,那么从名字上我们就知道我们这个二叉树的底层是使用链表来实现的,前面我们的二叉树是通过数组来实现的,那么在其是完全二叉树的情况下,此时我们使用数组来实现就会使得其空间浪费较少&a…...