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

【iOS】OC类与对象的本质分析

目录

    • 前言
    • clang常用命令
    • 对象本质探索
    • 属性的本质
    • 对象的内存大小
    • isa 指针探究


前言

OC 代码的底层实现都是 C/C++代码,OC 的对象都是基于 C/C++ 的数据结构实现的,实际 OC 对象的本质就是结构体,那到底是一个怎样的结构体呢?

clang常用命令

clang是一个C语言、C++、Objective-C语言的轻量级编译器,是由Apple主导编写的
clang主要用于把源文件编译成底层文件,比如把main.m 文件编译main.cppmain.o或者可执行文件

我们可以在终端使用 clang 命令使OC代码转换成 C++ 代码:

# 将 main.m 转换成 main.cpp 文件
clang -rewrite-objc main.m -o main.cpp

打开此 main.cpp 文件,共有 6 万多行代码:

在这里插入图片描述

实际上,不同平台支持的代码肯定是不一样的:Windows 、 MacOS 、 iOS 等,模拟器(i386)、 32bit(armv7)、 64bit(arm64)等,如果要指定平台(比如在 arm64 下的 iOS 开发),应在终端输入以下xcrun命令:

# xcrun 命令基于 clang 进行了封装更好用# 真机编译
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
# 模拟器编译
xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp

打开生成的 main-arm64.cpp 文件:

请添加图片描述

行数缩减到了 2 万多行,显然删减掉了冗余的代码

对象本质探索

将含有以下代码的 OC 文件转换成 Cpp 文件查看源码:

OC 代码:

@interface Student : NSObject {@publicint _no;int _age;
}@end

Cpp 代码:

在这里插入图片描述

可以看到 Student 的定义(IMPL,即 implementation 实现的意思)其实是 struct ,也就是对象的本质是结构体
在此结构体的定义中,有一个struct NSObject_IMPL结构体变量NSObject_IVARS,表明 Student 继承于 NSObject 类(子类的结构都包含父类的结构),即包括 NSObject 的成员变量

在这里插入图片描述

struct NSObject_IMPL的定义中,可以看到只有isa一个成员,其类型是 Class ,我们再找到 Class 的定义:

在这里插入图片描述

Class类型实际是一个objc_class类型的结构体指针,我们还可以看到常用的id是objc_object结构体指针类型,以及SEL是objc_selector(函数)的一个结构体指针

属性的本质

@interface Person : NSObject
@property (nonatomic, assign)int height;  //  自动生成实例变量、 setter/getter 方法
@end@implementation Person
@end

那么 setter/getter 方法在哪儿呢?
创建出来的实例对象里面只存有实例变量,方法不会放在实例对象里面,而放在类对象中,有关对象的分类因为方法是公用的,方法的代码都是一样的

对象的内存大小

现在我们知道对象的本质其实就是一个结构体,其内存大小与成员变量有关,即isa+其他成员变量。对象第 1 个成员变量就是isa结构体指针,而结构体的地址就是第 1 个成员变量的地址,即isa变量的地址

下面我们来看看一个NSObject对象占用多少内存,NSObject只有一个成员变量,也就是结构体指针变量isa

在 C/C++ 中,结构体指针的内存占用大小通常取决于操作系统和计算机的体系结构,与结构体本身的内容和大小无关。具体来说:

  • 在32位系统上,指针通常占用4字节。
  • 在64位系统上,指针通常占用8字节。
    这个大小是指针自身的大小,与指向的结构体中包含的数据类型或数量无关。指针的主要功能是存储内存地址,所以它的大小应该与系统的地址长度一致

现在绝大多数计算机的都是 64 位,所以 isa 变量占用 8 个字节,也就是说 NSObject 对象占用 8 字节

检验对象到底占用了多少内存,引入以下两个库:

#import <objc/runtime.h>
#import <malloc/malloc.h>

使用其中的 API :

NSObject* obj = [NSObject alloc] init];//  下面两个函数的返回值都是size_t(unsigned int),所以占位符使用%zd
NSLog(@"%zd", class_getInstanceSize([NSObject class]));//NSLog(@"%zd", malloc_size(CFBridgingRetain(obj)));
NSLog(@"%zd", malloc_size((__bridge const void *)(obj)));  //OC转C 要进行桥接__bridge

运行结果:

在这里插入图片描述

刚分析 NSObject 对象不应该占用 8 字节吗?为什么第 2 个函数返回了 16 个字节?

实际class_getInstanceSize()返回的是类实例对象的成员变量大小,malloc_size()返回的是传入对象指针所指向的内存大小

下面来看看class_getInstanceSize()的实现:

size_t class_getInstanceSize(Class cls)
{if (!cls) return 0;return cls->alignedInstanceSize();
}

在这里插入图片描述

查看最终的返回值,如果字节数小于 16 ,通过这种方法返回的结果就是 16,可见给 NSObject 分配了 16 个字节,但真正利用起来的只有 8 个字节(用来存放 isa 成员)
注释也有提到:

  1. CoreFoundation框架要求所有对象至少占用 16 字节,这种硬性规定
  2. 返回的是内存对齐后的成员变量所占内存大小

所以一般不去过多地关注此函数,而是通过malloc_size()准确的了解到对象占用内存大小

打断点通过 LLDB 查看内存分布:

在这里插入图片描述

上面以十六进制的形式打出:

LLDB 指令:
print 、 p:打印
po:打印对象
——
读取内存分布:memory read/数量 格式 字节数 内存地址x/数量 格式 字节数 内存地址
格式:x是 16 进制,f是浮点,d是 10 进制;字节大小:b是byte 1字节,w是word 4字节,h是half word 2字节,g是 giant word 8字节
——
修改内存中的值:memory write 内存地址 数值

通过 ViewMemory 实时查看内存数据:

在这里插入图片描述

在此处输入内存地址:
在这里插入图片描述
在这里插入图片描述

不论是通过 LLDB 还是 ViewMemory 都可以查看到标灰的位置就是obj对象的内存分布,后面的都是其他不相关的内存分布
由于一个 16 进制位代表 4 个二进制位,两个就代表 8 个二进制位(1 个字节)
71 20 32 E3 01 00 00 01 00 00 00 00 00 00 00 00,16 个字节中实际就只有 8 个字节有数据(成员变量,这里指 isa 指针变量)
0xe3322071 0x01000001 0x00000000这是地址分布,怎么跟上面的顺序是相反的呢?这里涉及到一个大小端的问题:

大小端(Endian)是指数据在计算机内存中的存储方式,具体来说是多字节数据的存储顺序问题。它主要涉及两种模式:大端(Big Endian)和小端(Little Endian)。
大端: 数据的高位字节存储在内存的低地址端,低位字节存储在高地址端。也就是说,数据的第一个字节(最重要的字节)存储在起始地址上。例如,如果有一个32位的整数0x12345678存储在地址0x1000开始的位置上,其存储顺序如下:

  • 0x1000: 0x12
  • 0x1001: 0x34
  • 0x1002: 0x56
  • 0x1003: 0x78
    大端模式直观地反映了数据在文档和其他数字表示中的顺序,因此在阅读内存转储时容易理解。很多网络协议(如TCP/IP)采用大端模式来传输数据。

小端: 数据的低位字节存储在内存的低地址端,高位字节存储在高地址端。也就是说,数据的最后一个字节(最重要的字节)存储在起始地址上。同样地,如果有一个32位的整数0x12345678存储在地址0x1000开始的位置上,其存储顺序如下:

  • 0x1000: 0x78
  • 0x1001: 0x56
  • 0x1002: 0x34
  • 0x1003: 0x12
    小端模式在某些类型的处理器(如x86架构)中使用,因为它可以在处理器将数据加载到寄存器时减少字节重排的操作。

一些系统允许在大端和小端之间进行配置,这种灵活性在处理多种硬件平台时特别有用。同时,还存在一些更复杂的端模式,例如双端模式或中间端模式,这些通常用于特定的应用或硬件。
端序的理解对于开发者在处理字节级操作时至关重要,尤其是在进行网络编程、读写文件以及在不同架构之间迁移代码时。不匹配的字节序会导致数据解释错误,从而引发程序错误和数据损坏。因此,开发者必须确保在这些操作中考虑到端序的因素。

总结: 创建一个实例对象,至少需要内存class_getInstanceSize(对齐后),实际分配内存malloc_size

最后以下例说明内存对齐:

struct Person_IMPL {struct NSObject_IMPL NSOBJECT_IVARS;  // 8int _age;  // 4int _height;  // 4int _no;  // 4
};  //  和为20,内存对齐交后:24@interface Person : NSObject {int _age;int _height;int _no;
}@end@implementation Person@endvoid testAllocSize(void) {NSLog(@"%zd", sizeof(struct Person_IMPL));  //同class_getInstanceSize 24// sizeof 运算符关键字 不是一个函数 编译过程中就会查看传入的类型并识别数据类型字节大小 编译时就会确定为一个常数//  内存分配注意的地方Person* person = [[Person alloc] init];NSLog(@"%zd %zd", class_getInstanceSize([Person class]), malloc_size((__bridge const void *)(person)));  // 24 32// 操作系统在分配内存时,也会有“内存对齐”,所以 size 是 24,返回 32NSLog(@"%zd", sizeof(person));  // 8 指的是person指针本身的大小
}

isa 指针探究

之前在【iOS】alloc、init和new原理文章中提到了对象通过obj->initInstanceIsa初始化关联isa指针,下面就来看看isa的结构,如何通过它关联类

initInstanceIsa方法

inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{ASSERT(!cls->instancesRequireRawIsa());ASSERT(hasCxxDtor == cls->hasCxxDtor());initIsa(cls, true, hasCxxDtor);
}

initIsa方法

inline void 
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{ ASSERT(!isTaggedPointer()); isa_t newisa(0);  //  isa初始化if (!nonpointer) {  //  非nonpointer指针直接绑定clsnewisa.setClass(cls, this);} else {ASSERT(!DisableNonpointerIsa);ASSERT(!cls->instancesRequireRawIsa());#if SUPPORT_INDEXED_ISAASSERT(cls->classArrayIndex() > 0);newisa.bits = ISA_INDEX_MAGIC_VALUE;// isa.magic is part of ISA_MAGIC_VALUE// isa.nonpointer is part of ISA_MAGIC_VALUEnewisa.has_cxx_dtor = hasCxxDtor;newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#elsenewisa.bits = ISA_MAGIC_VALUE;// isa.magic is part of ISA_MAGIC_VALUE// isa.nonpointer is part of ISA_MAGIC_VALUE
#   if ISA_HAS_CXX_DTOR_BITnewisa.has_cxx_dtor = hasCxxDtor;
#   endifnewisa.setClass(cls, this);
#endif
#if ISA_HAS_INLINE_RCnewisa.extra_rc = 1;
#endif}// This write must be performed in a single store in some cases// (for example when realizing a class because other threads// may simultaneously try to use the class).// fixme use atomics here to guarantee single-store and to// guarantee memory order w.r.t. the class index table// ...but not too atomic because we don't want to hurt instantiationisa() = newisa;
}

initIsa方法中,如果是非nonpointer类型指针,则直接通过clssetClass方法绑定;否则现有一些位域bits赋值操作,再调setClass方法绑定

我们可以看到方法内创建了一个 isa_t 类型的newisa实例, 做了赋值操作newisa.bits = ISA_MAGIC_VALUE;后,返回了newisa
注释中写得很清晰,news.bits初始化时候只设置了nonpointermagic两个部分,其余部分都没有进行设置,故值都为默认值0
该源码解释了上面初始化isa的两种方式:

  • 通过cls初始化:非nonpointer,存储着Class、Meta-Class对象的内存地址信息
  • 通过bits初始化:nonpointer,进行一系列的初始化操作

请添加图片描述

isa_t联合体

#include "isa.h"union isa_t {isa_t() { }isa_t(uintptr_t value) : bits(value) { }uintptr_t bits;private:// Accessing the class requires custom ptrauth operations, so// force clients to go through setClass/getClass by making this// private.Class cls;public:
#if defined(ISA_BITFIELD)struct {ISA_BITFIELD;  // defined in isa.h};#if ISA_HAS_INLINE_RCbool isDeallocating() const {return extra_rc == 0 && has_sidetable_rc == 0;}void setDeallocating() {extra_rc = 0;has_sidetable_rc = 0;}
#endif // ISA_HAS_INLINE_RC#endifvoid setClass(Class cls, objc_object *obj);Class getClass(bool authenticated) const;Class getDecodedClass(bool authenticated) const;
};

isa_t是一个联合体类型,其中的变量有bitscls以及结构体里面的ISA_BITFIELD宏定义变量,ISA_BITFIELD结构如下,是按位域定义的:

在这里插入图片描述

各个变量的内存分布图:

在这里插入图片描述

各个变量的解释:

  • nonpointer:表示是否对 isa 指针开启指针优化,0:纯isa指针,1:不止是类对象地址,isa中包含了类信息、对象的引用计数等
  • has_assoc:关联对象标志位,0没有,1存在
  • has_cxx_dtor:该对象是否有C++Objc的析构器,如果有析构函数,则需要做析构逻辑,没有则可以更快的释放对象
  • shiftcls:存储类指针的值,开启指针优化的情况下,在ARM64架构中有33位来存储类指针值
  • magic:用于调试判断当前对象是真的对象还是未初始化的空间
  • weakly_referenced:标志对象是否被指向或曾经指向一个ARC的弱变量,没有弱引用对象则可以更快地释放
  • unused:对象是否释放
  • has_sidetable_rc:当对象引用计数大于10时,则需要借用该变量存储进位
  • extra_rc:对象的引用计数值(实际上是引用计数减1),例如:引用计数是10,那么extra_rc为9,如果引用计数大于10,则需要使用到has_sidetable_rc

总结

  • isa正是通过以上联合体中的变量信息将类关联起来
  • isa指针分为nonpointer和非nonpointer类型,非nonpointer类型(0)就是一个纯指针,nonpointer类型(1)指针开启了指针优化
  • 指针优化的设计,更好地利用isa的内存,程序中有大量的对象,针对isa的优化节省了大量内存

相关文章:

【iOS】OC类与对象的本质分析

目录 前言clang常用命令对象本质探索属性的本质对象的内存大小isa 指针探究 前言 OC 代码的底层实现都是 C/C代码&#xff0c;OC 的对象都是基于 C/C 的数据结构实现的&#xff0c;实际 OC 对象的本质就是结构体&#xff0c;那到底是一个怎样的结构体呢&#xff1f; clang常用…...

【机器学习】使用Python的dlib库实现人脸识别技术

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、引言二、传统人脸识别技术1. 基于几何特征的方法2. 基于模板匹配的方法3. 基于统计学习的方法 三、深度学习在脸识别中的应用1. 卷积神经网络&#xff08;CNN&#xff09;2. FaceNet和ArcFace 四、使用Python和dlib库实…...

GitHub 令牌泄漏, Python 核心资源库面临潜在攻击

TheHackerNews网站消息&#xff0c;软件供应链安全公司 JFrog 的网络安全研究人员称&#xff0c;他们发现了一个意外泄露的 GitHub 令牌&#xff0c;可授予 Python 语言 GitHub 存储库、Python 软件包索引&#xff08;PyPI&#xff09;和 Python 软件基金会&#xff08;PSF&…...

【面试题】Golang 锁的相关问题(第七篇)

目录 1.Mutex 几种状态 1. 锁定状态&#xff08;Locked&#xff09; 2. 未锁定状态&#xff08;Unlocked&#xff09; 3. 唤醒状态&#xff08;Woken&#xff09; 4. 饥饿状态&#xff08;Starving&#xff09; 5. 等待者计数&#xff08;Waiters Count&#xff09; 总结…...

深入剖析CommonJS modules和ECMAScript modules

目录 前言CommonJS&#xff1a;服务器端模块化的先驱背景与起源语法与机制 ECMAScript Modules&#xff1a;现代前端的基石背景与起源语法与机制 比较与权衡语法差异加载机制编译时与运行时运行时行为构建第三方库现代开发环境 结论 前言 在 JavaScript 生态系统中&#xff0c…...

角点检测及MATLAB实现

一、角点简介 角点通常指的是两条直线构成角时的交点。‌在更广泛的应用中&#xff0c;‌角点这一概念也被扩展到数字图像处理领域&#xff0c;‌其中角点被定义为图像中物体轮廓线的连接点&#xff0c;‌这些点在某方面属性特别突出&#xff0c;‌即在某些属性上强度最大或者最…...

TypeScript导学:从零开始

引言 TypeScript的背景 TypeScript是一种由微软开发的开源编程语言&#xff0c;它是JavaScript的一个超集&#xff0c;添加了可选的静态类型和基于类的面向对象编程。自2012年首次发布以来&#xff0c;TypeScript因其能够提高代码的可读性、可维护性和可扩展性而迅速获得了广…...

【BUG】已解决:IndexError: list index out of range

已解决&#xff1a;IndexError: list index out of range 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;武汉城市开发者社区主…...

AWS-S3实现Minio分片上传、断点续传、秒传、分片下载、暂停下载

文章目录 前言一、功能展示上传功能点下载功能点效果展示 二、思路流程上传流程下载流程 三、代码示例四、疑问 前言 Amazon Simple Storage Service&#xff08;S3&#xff09;&#xff0c;简单存储服务&#xff0c;是一个公开的云存储服务。Web应用程序开发人员可以使用它存…...

Selenium - 设置元素等待及加载策略

7月18日资源分享&#xff1a; 耿直哥三部曲全——机器学习&#xff0c;强化学习&#xff0c;深度学习 链接: https://pan.baidu.com/s/1c_eVVeqCZmB6zszHt6ZXiw?pwdtf2a 在使用Selenium进行网页自动化测试时&#xff0c;一个常见的问题是页面加载速度和元素的可见性问题。…...

【数据结构】线性结构——数组、链表、栈和队列

目录 前言 一、数组&#xff08;Array&#xff09; 1.1优点 1.2缺点 1.3适用场景 二、链表&#xff08;Linked List&#xff09; 2.1优点 2.2缺点 2.3适用场景 三、栈&#xff08;Stack&#xff09; 3.1优点 3.2缺点 3.3适用场景 四、队列&#xff08;Queue&#xff09; 4.1优点…...

json将列表字典等转字符串,然后解析又转回来

在 Python 中使用 json 模块来方便地在数据和 JSON 格式字符串之间进行转换&#xff0c;以便进行数据的存储、传输或与其他支持 JSON 格式的系统进行交互。 JSON 字符串通过 json.loads() 函数转换为 Python 对象。 pthon对象通过json.dumps()转为字符串 import jsonstr_list…...

记录|.NET上位机开发和PLC通信的实现

本文记录源自&#xff1a;B站视频 实验结果&#xff1a;跟视频做下来是没有问题的。能运行。 自己补充做了视频中未实现的读取和写入数据部分【欢迎小伙伴指正不对的地方】 目录 前言一、项目Step1. 创建项目Step2. 创建动态图片展示Step3. 创建图片型按钮Step4. 创建下拉框Ste…...

微服务实战系列之玩转Docker(二)

前言 上一篇&#xff0c;博主对Docker的背景、理念和实现路径进行了简单的阐述。作为云原生技术的核心之一&#xff0c;轻量级的容器Docker&#xff0c;受到业界追捧。因为它抛弃了笨重的OS&#xff0c;也不带Data&#xff0c;可以说&#xff0c;能够留下来的都是打仗的“精锐…...

Linux:信号的概念与产生

信号概念 信号是进程之间事件异步通知的一种方式 在Linux命令行中&#xff0c;我们可以通过ctrl c来终止一个前台运行的进程&#xff0c;其实这就是一个发送信号的行为。我们按下ctrl c是在shell进程中&#xff0c;而被终止的进程&#xff0c;是在前台运行的另外一个进程。因…...

云监控(华为) | 实训学习day2(10)

spring boot基于框架的实现 简单应用 - 用户数据显示 开发步骤 第一步&#xff1a;文件-----》新建---项目 第二步:弹出的对话框中,左侧选择maven,右侧不选任何内容. 第三步&#xff0c;选择maven后&#xff0c;下一步 第4步 &#xff1a;出现对话框中填写项目名称 第5步&…...

数据结构第35节 性能优化 算法的选择

算法的选择对于优化程序性能至关重要。不同的算法在时间复杂度、空间复杂度以及适用场景上有着明显的差异。下面我将结合具体的代码示例&#xff0c;来讲解几种常见的算法选择及其优化方法。 示例 1: 排序算法 场景描述: 假设我们需要对一个整数数组进行排序。 算法选择: …...

每天一个数据分析题(四百三十六)- 正态分布

X为服从正态分布的随机变量N(2, 9), 如果P(X>c)P(X<c), 则c的值为&#xff08;&#xff09; A. 3 B. 2 C. 9 D. 2/3 数据分析认证考试介绍&#xff1a;点击进入 题目来源于CDA模拟题库 点击此处获取答案 数据分析专项练习题库 内容涵盖Python&#xff0c;SQL&…...

跟我学C++中级篇——虚函数的性能

一、虚函数性能 一般来说&#xff0c;面向对象的设计中&#xff0c;继承和多态是其中两个非常重要的特征。从使用的过程来看&#xff0c;一般应用到继承的&#xff0c;使用多态的可能性就非常大。而多态的实现有很多种&#xff0c; 但开发者通常认为的多态&#xff08;动多态&…...

trl - 微调、对齐大模型的全栈工具

文章目录 一、关于 TRL亮点 二、安装1、Python包2、从源码安装3、存储库 三、命令行界面&#xff08;CLI&#xff09;四、如何使用1、SFTTrainer2、RewardTrainer3、PPOTrainer4、DPOTrainer 五、其它开发 & 贡献参考文献最近策略优化 PPO直接偏好优化 DPO 一、关于 TRL T…...

GuLi商城-商品服务-API-品牌管理-品牌分类关联与级联更新

先配置mybatis分页&#xff1a; 品牌管理增加模糊查询&#xff1a; 品牌管理关联分类&#xff1a; 一个品牌可以有多个分类 一个分类也可以有多个品牌 多对多的关系&#xff0c;用中间表 涉及的类&#xff1a; 方法都比较简单&#xff0c;就不贴代码了...

【linux】服务器ubuntu安装cuda11.0、cuDNN教程,简单易懂,包教包会

【linux】服务器ubuntu安装cuda11.0、cuDNN教程&#xff0c;简单易懂&#xff0c;包教包会 【创作不易&#xff0c;求点赞关注收藏】 文章目录 【linux】服务器ubuntu安装cuda11.0、cuDNN教程&#xff0c;简单易懂&#xff0c;包教包会一、版本情况介绍二、安装cuda1、到官网…...

在 Apifox 中如何高效批量添加接口请求 Body 参数?

在使用 Apifox 进行 API 设计时&#xff0c;你可能会遇到需要添加大量请求参数的情况。想象一下&#xff0c;如果一个接口需要几十甚至上百个参数&#xff0c;若要在接口的「修改文档」里一个个手动添加这些参数&#xff0c;那未免也太麻烦了&#xff0c;耗时且易出错。这时候&…...

专业PDF编辑工具:Acrobat Pro DC 2024.002.20933绿色版,提升你的工作效率!

软件介绍 Adobe Acrobat Pro DC 2024绿色便携版是一款功能强大的PDF编辑和转换软件&#xff0c;由Adobe公司推出。它是Acrobat XI系列的后续产品&#xff0c;提供了全新的用户界面和增强功能。用户可以借助这款软件将纸质文件转换为可编辑的电子文件&#xff0c;便于传输、签署…...

车载音视频App框架设计

简介 统一播放器提供媒体播放一致性的交互和视觉体验&#xff0c;减少各个媒体应用和场景独自开发的重复工作量&#xff0c;实现媒体播放链路的一致性&#xff0c;减少碎片化的Bug。本文面向应用开发者介绍如何快速接入媒体播放器。 主要功能&#xff1a; 新设计的统一播放U…...

StarRocks on AWS Graviton3,实现 50% 以上性价比提升

在数据时代&#xff0c;企业拥有前所未有的大量数据资产&#xff0c;但如何从海量数据中发掘价值成为挑战。数据分析凭借强大的分析能力&#xff0c;可从不同维度挖掘数据中蕴含的见解和规律&#xff0c;为企业战略决策提供依据。数据分析在营销、风险管控、产品优化等领域发挥…...

VUE中setup()

在Vue中&#xff0c;setup() 函数是Vue 3.0及更高版本引入的一个重要特性&#xff0c;它是Composition API的入口点。setup() 函数用于初始化组件的状态和逻辑&#xff0c;包括定义响应式数据、方法和生命周期钩子。以下是关于setup() 函数的详细解释&#xff1a; 1. 作用与特…...

【单元测试】SpringBoot

【单元测试】SpringBoot 1. 为什么单元测试很重要&#xff1f;‼️ 从前&#xff0c;有一个名叫小明的程序员&#xff0c;他非常聪明&#xff0c;但有一个致命的缺点&#xff1a;懒惰。小明的代码写得又快又好&#xff0c;但他总觉得单元测试是一件麻烦事&#xff0c;觉得代码…...

分布式搜索引擎ES-elasticsearch入门

1.分布式搜索引擎&#xff1a;luceneVS Solr VS Elasticsearch 什么是分布式搜索引擎 搜索引擎&#xff1a;数据源&#xff1a;数据库或者爬虫资源 分布式存储与搜索&#xff1a;多个节点组成的服务&#xff0c;提高扩展性(扩展成集群) 使用搜索引擎为搜索提供服务。可以从海量…...

TCP三次握手与四次挥手详解

1.什么是TCP TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的通信协议&#xff0c;属于互联网协议族&#xff08;TCP/IP&#xff09;的一部分。TCP 提供可靠的、顺序的、无差错的数据传输服务&…...

网站被抄袭/推广软文平台

搭建好简单版本的feign客户端与服务端&#xff0c;本文基于简单版本进行扩展&#xff0c;支持客户端支持gzip。 1.修改配置文件开启gizp feign:client:config:default:connectTimeout: 5000readTimeout: 5000loggerLevel: basiccompression:request:enabled: true #开启gz…...

爱做网站/百度推广营销

最近我们部门来了一个实习生&#xff0c;天天手里捧着一本《Kotlin从入门到精通》在那儿埋头苦读&#xff0c;等一个月都快过去了&#xff0c;我跑去问他能不能开始接项目了。他非常为难的拿着书和我说他只看了一半&#xff0c;项目代码还没看懂&#xff0c;远远没能到能做项目…...

网站 外包方案/怎么创建网站平台

题目描述 小明很喜欢3和5这两个数字&#xff0c;他将能被3或5整除的数叫做美丽数。现在给你一个整数N&#xff08;1<N<100000&#xff09;&#xff0c;你能告诉小明第N个美丽数是多少吗&#xff1f; 输入 输入包含多组测试数据。每组输入一个整数N&#xff08;1<N<…...

做网站需不需要服务器/青岛网站制作公司

一、Kafka核心总控制器Controller 在Kafka集群中会有一个或者多个broker&#xff0c;其中有一个broker会被选举为控制器&#xff08;Kafka Controller&#xff09;&#xff0c;它负责管理整个集群中所有分区和副本的状态。 当某个分区的leader副本出现故障时&#xff0c;由控…...

公司的网站建设费用属于什么费/seo优化排名百度教程

企业Java开发人员想要一个快速的Web框架解决方案&#xff0c;而又不增加日常开发工作的想法&#xff0c;这并不是什么新鲜事物。 现在提供了多个选项&#xff0c;允许开发人员即时创建UI。 快速开发平台的一个例子就是OpenXava&#xff0c;它使开发人员可以完全通过编写Java或G…...

北京响应式网站建设费用/高级搜索入口

旋转字符串 解法一&#xff1a;暴力移位法 初看此题&#xff0c;可能最先想到的方法是按照题目所要求的&#xff0c;把需要移动的字符一个一个地移动到字符串的尾部&#xff0c;如此我们可以实现一个函数LeftShiftOne(char* s, int n) &#xff0c;以完成移动一个字符到字符串…...