《Effective Objective-C》阅读笔记(上)
目录
高质量iOS之熟悉OC
了解OC语言的起源
在类的头文件中尽量少引入其他头文件
多用字面语法,少用与之等价的方法
字面数值
字面量数组
字面量字典
局限性
多用类型常量,少用#define预处理指令
用枚举表示状态、选项、状态码
高质量iOS之对象、消息、运行期
理解”属性“这一概念
属性特质
原子性
读/写权限
内存管理语义
方法名
在对象内部尽量直接访问实例变量
理解“对象等同性”这一概念
特定类的等同性判定方法
等同性判定的执行深度
容器中可变类的等同性
以“类族模式”隐藏实现细节
在既有类中使用关联对象存放自定义数据
理解objc_msgSend的作用
理解消息转发机制
动态方法解析
备援接收者
完整的消息转发
消息转发全流程
用“方法调配技术”调试“黑盒方法”
理解“类对象”的用意
高质量iOS之熟悉OC
了解OC语言的起源
OC语言由Smalltalk演化而来,后者是消息型语言的鼻祖,使用“消息结构”而非“函数调用”。
两者之间的区别看上去就像上面这样。
关键区别在于:使用消息结构的语言,其运行时所应执行的代码由运行环境来决定;而使用函数调用的语言,则由编译器决定。
OC是C的“超集”,所以C语言中所有功能在编写OC代码时依然适用。理解C语言的内存模型,有助于理解OC的内存模型及其“引用计数”机制的工作原理。
OC语言中的指针是用来指示对象的,要声明某个变量,令其指代某个对象,可用如下语法:
这种语法就是照搬C语言的,声明了一个指向NSString的指针,所有OC对象都必须这样声明,因为对象所占内存总是分配在“堆空间”中,而不会分配在“栈”上。
分配在堆中的内存必须直接管理,而分配在栈上用于保存变量的内存则会在其栈帧弹出时自动清理。OC将对内存管理抽象出来不再需要用malloc及free来分配1或释放内存,而是在运行期把这部分工作抽象为一套内存管理架构,名叫“引用计数”。
在OC代码中,有时遇到定义里不含*的变量,它们可能使用“栈空间”,所保存的不是OC对象。
在类的头文件中尽量少引入其他头文件
OC与C和C++一样也使用”头文件“与”实现文件“来区隔代码。用OC语言编写任何类几乎都需要引入Foudation.h。如果不引入这个文件,就要引入与其超类所属框架相对应的“基本头文件”。比如UIViewController的子类的头文件需要引入UIKit.h
#import "EOCEmployer.h"
当某个类需要声明其他类为属性时,以前我们通常会使用上述代码来引入某个类的头文件。
但其实在编译当前类时,我们并不需要知道它属性中的类也就是EOCEmployer类的全部细节,只要知道有一个类叫做EOCEmployer就好,可以采用以下办法:
@class EOCEmployer;
这叫做“向前声明”该类。
而当前类的实现文件则需引入EOCEmployer类的头文件,因为如果要使用EOCEmployer,就必须知道其所有接口细节。
这样向前声明不仅可以节约编译时间,也解决了两个类互相引用的问题。
但是有时候必须引入其他头文件:如果类继承某个超类以及遵从某个协议时,必须要引入定义那个超类或协议的头文件。
因为这时,协议必须有完整定义,要知道该协议中定义的方法。
除了例如“委托协议”的有些协议,最好把协议单独放在一个头文件中。
委托协议只有协议和接受协议委托类放在一起定义才有意义,此时最好在实现文件中声明此类实现了该委托协议,并把这段实现的代码放在分类中。这样只要在实现文件引入包含委托协议的头文件即可
多用字面语法,少用与之等价的方法
字面语法是一种更精简声明NSString、NSNumber、NSArray、NSDictionary类的实例的语法,使用这种语法可以缩减源代码长度,使其更为易读。
字面数值
以下是使用字面量来创建NSNumber的语法:
NSNumber *intNumber = @1;
NSNumber *floatNumber = @2.5f;
NSNumber *doubleNumber = @3.14159;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';
//等号右边还可以是表达式
int x = 5;
float y = 6.23f;
NSNumber *expressionNumber = @(x * y);
字面量数组
使用字面量创建数组:
NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];
//还可以使用字面量取下标
NSString* dog = animals[1];
使用字面量语法,如果元素对象中有nil,则会抛出异常。
字面量字典
NSDictionary *personData = @{@"firstName":@"Matt",@"lastName":@"Galloway",@"age":@23};
//取键值
NSString *lastName = personData[@"lastName"];
与字面量数组相同,如果有值为nil,便会抛出异常
如果数组和字典可变,那么也可以用字面语法来修改元素值。
局限性
字面量不适用于以上类的自定义子类。
并且字面语法创建出来的对象都是不可变的。
多用类型常量,少用#define预处理指令
编写代码时经常要定义常量,有一种方法是使用#define预处理命令
#define ANIMATION_DURATION 0.3
这种方法一般可以实现想要的效果,但是这样定义的常量没有类型信息,并且如果使用这种方法,预处理会把碰到的所有ANIMATION_DURATION都替换成0.3,这样的话,假设此指令声明在某个头文件中,那么所有引入了这个头文件的代码,ANIMATION_DURATION都会被替换。
因此定义常量是更好的选择,比如:
static const NSTimeInterval kAnimationDuration = 0.3
这行代码定义了一个类型为NSTimeInterval的常量
要注意常量名称,常用的命名法是:若常量局限于某”编译单元“(“实现文件”)之内,则在前面加字母k,若在类之外可见,则也类名为前缀。
定义的位置也很重要,不应定义在头文件中,如果常量定义在头文件中,相当于声明了一个全局变量,应该加上前缀来表明所属的类。
如果不打算公开某个常量,则应将其定义在使用该常量的实现文件里。
变量一定要同时用static与const来声明。const确保变量不会被修改,而static将变量限制在当前编译单元。
如果常量需要公开,定义方式有所不同:
//header file
extern NSString *const EOCStringConstant
//implementation file
NSString *const EOCStringConstant = "VALUE";
extern这个关键字可以告诉编译器在全局符号表中有一个名叫EOCStringConstant的符号。
由于要放在全局符号表里,所以命名要谨慎。为避免名称冲突,最好用与之相关的类名作前缀。
用枚举表示状态、选项、状态码
在以一系列常量来表示错误状态码或可组合的选项时,宜使用枚举为其命名。枚举是一种常量命名方式,某对象经历的各种状态可以定义为一个简单的枚举集。比如:
编译器为每个枚举分配一个独有的编号,从0开始,每个枚举递增1。
定义枚举的语法如下:
enum EOCConnectionState state = EOCConnectionStateDisconnected;
//可用typedef关键字定义,这样就不用每次敲入enum了
enum EOCConnectionState {EOCConectionStateDisconnected,EOCConectionStateConnecting,EOCConectionStateConnected,
};
typedef enum EOCConnectionState EOCConectionState;
可以指定枚举使用哪种底层数据类型,语法如下:
enum EOCConnectionStateConnectionState : NSInteger;
//指定底层数据类型为NSInteger
//还可以不使用编译器分配的序号,手工指定某个枚举成员对应的值
enum EOCConnectionStateConnectionState {EOCConnectionStateDisconnected = 1,EOCConnectionStateConnecting,EOCConnectionStateConnected,
};
定义选项也应使用枚举类型,若选项可以彼此组合,更应如此。只要枚举定义得对,各选项可通过“按位或操作符”来组合。比如:
enum UIViewAutoresizing {UIViewAutoresizingNone = 0,UIViewAutoresizingFlexibleLeftMargin = 1 << 0,UIViewAutoresizingFlexibleWidth = 1 << 1,UIViewAutoresizingFlexbleRightMargin = 1 << 2,UIViewAutoresizingFlexibleTopMargin = 1 << 3,UIViewAutoresizingFlexibleHeight = 1 << 4,UIViewAutoresizingFlexibleBottomMargin = 1 << 5,
}
这样每个选项都可启用或禁用,因为每个枚举值对应的二进制表示中只有一位是1,可以用“按位与操作符”判断是否已启用某个选项。
Foundation框架中有一些宏可以用来定义这些枚举类型并指定底层数据类型,用法如下:
typedef NS_ENUM(NSUInteger, EOCConnectionState) {EOCConnectionStateDisconnected,EOCConectionStateConnecting,EOCConectionStateConnected,
};
typedef NS_OPTIONS(NSUInteger, EOCPermittedDirection) {EOCPermittedDirectionUp = 1 << 0,EOCPermittedDirectionDown = 1 << 1,EOCPermittedDirectionLeft = 1 << 2,EOCPermittedDirectionRight = 1 << 3,
};
需要注意,凡是需要以按位或操作来组合的枚举都应使用NS_OPTIONS定义,若枚举不需要互相组合,则应使用NS_ENUM来定义。
枚举还可以放在switch语句里,要注意在switch语句中,如果用枚举来定义状态集,则最好不要有default分支,这样如果加入新的状态,会有警告信息。
高质量iOS之对象、消息、运行期
理解”属性“这一概念
属性是一种用@property语法来定义的用来封装对象中的数据的变量。
属性特质
属性特质分为四类:
原子性
如果具备nonatomic特质,则不使用同步锁,如不声明nonatomic特质,默认为atomic。
读/写权限
readwrite和readonly两个特质分别表示拥有存取方法和只有获取方法
内存管理语义
编译器合成存取方法时,要根据此特质来决定生成的代码。
assing “设置方法”只会执行针对“纯量类型”的简单赋值
strong 先保留新值,再释放旧值,再将新值设置上去
weak 不保留新值,不释放旧值,与assign类似。属性所指对象摧毁时,属性值清空
unsafe_unretained 与assign相同,但适用于对象类型,摧毁时不清空,与weak相反
copy 与strong类似,但不保留新值,而是将其“拷贝”
方法名
即存取方法
在实现初始化方法时,一定遵循属性定义中的语义
在对象内部尽量直接访问实例变量
在对象之外访问实例变量时总是应该通过属性来做,而在对象内部,推荐的做法是:读取实例变量时采取直接访问的形式,而设置实例变量时通过属性来做。
有一些特殊情况:
1.在初始化方法中,总是应该直接访问实例变量。如果待初始化的实例变量声明在超类中,子类中无法直接访问,则调用设置方法。
2.惰性初始化必须通过“获取方法”来访问属性,否则永远不会初始化。
理解“对象等同性”这一概念
对象等同性判定方法与==操作符不同,==比较的是两个指针本身。"isEqual"方法可以判断对象的等同性,某些对象有特殊的“等同性判定方法”。
NSObject协议中有两个用于判断等同性的关键方法:
- (BOOL)isEqual:(id)object;
- (NSUInteger)hash;
默认实现是:当且仅当指针值相等时,对象才相等。
如果想覆写方法,那就要理解其约定
如果"isEqual"方法判定相等,那么hash方法也必须返回同一个值,如果hash返回同一个值,isEqual方法未必判定相等。
编写hash方法时,应该用当前对象做做实验,以便减少碰撞频度与降低运算复杂程度之间取舍。
特定类的等同性判定方法
在编写判定方法时,应一并覆写"isEqual"方法,实现方式为:如果受测的参数与接受该消息的对象都属于一个类,就调用自己编写的判定方法,否则交给超类来判断。
等同性判定的执行深度
创建等同性判定方法时,需要决定是整个对象还是根据其中几个字段。NSArray对比方法是,先看对象个数,再对比每个对象,这叫做“深度等同性判定”。可以创建标识符来帮助判定等同性。
容器中可变类的等同性
在容器中放入可变类对象时,放入collection之后,就不应再改变其哈希码了。
以“类族模式”隐藏实现细节
“类族”是一种很有用的模式,可以隐藏“抽象基类”背后的实现细节。类族中的所有类基于一个基类,并且一般不允许直接创建。
判断是否为类族中的类,不要检测两个类对象是否等同,而是应该采用isKindOfClass:方法。
从公共抽象基类中继承子类时应先阅读开发文档。
如NSArray的类族要新增子类,要遵守几条规则:
1.子类应继承自类族中抽象基类
2.子类应定义自己的数据存储方式
3.子类应覆写超类文档中指明需要覆写的方法
在既有类中使用关联对象存放自定义数据
关联对象相当于把对象变成字典,不同的键对应不同的值。设置关联对象值时,通常使用静态全局变量做键。
存储关联对象时,可以指明存储策略,来维护内存管理语义。
下列方法管理关联对象:
理解objc_msgSend的作用
在对象上调用方法用OC术语来说叫“传递信息”。OC中对象收到消息后,调用哪个方法于运行期决定,因此是一门动态语言。
OC中将方法转变为消息调用的是objc_msgSend这个函数
这个函数会依据接收者与选择子的类型来调用适当的方法。先搜寻方法列表,若能找到相符合的,就跳至实现代码,若找不到,就沿着继承体系向上查找,找到合适的方法之后再跳转。如果最后还找不到,就执行“消息转发”。
理解消息转发机制
消息转发分为两大阶段,第一阶段先征询接收者,看是否能动态添加方法,这叫做“动态方法解析”。第二阶段分为两小步:首先查看有没有其他对象可以处理该消息,有的话就会转给那个对象。如果没有,就启动完整的转发机制,吧消息封装到NSInvocation对象中,令接收者设法解决当前未处理的这条信息。
动态方法解析
对象收到无法解决的消息后,首先调用类方法:
使用这种方法的前提是:相关方法的实现代码已经写好,只等着运行时动态插在类里面。
备援接收者
当前接收者可以处理未知的选择子,这一步系统会问接收者是否可以把这条消息装给其他接收者来处理:
若找到备援对象,就将其返回;若找不到,就返回nil。
我们无法操作这一步转发的消息,若想先修改消息内容再发送,就得启用完整的消息转发机制。
完整的消息转发
这里将消息封装在NSInvacation对象中,并调用方法来转发信息:
消息转发全流程
每一步均有机会处理消息,步骤越往后,处理消息的代价就越大。
用“方法调配技术”调试“黑盒方法”
可以在运行期改变对象给定的选择子名称对应的方法,这被称为“方法调配”。
比如可以交换两个方法实现,可以通过下列方法:
通过此方案,可以为那些完全不知道具体实现的黑盒方法增加日志记录功能,有助于程序调试。
理解“类对象”的用意
描述OC对象所用的数据结构定义在运行期程序库的头文件里,id类型也定义在这里:
该结构体首个成员是Class类变量,定义了对象的类,称为"isa"指针。Class对象也定义在这个头文件中:
说明Class本身也是OC对象,类对象所属的类型是”元类“。每个类仅有一个类对象。
在查询类型信息时,尽量使用类型信息查询方法,而不要直接比较两个类对象是否等同。
相关文章:

《Effective Objective-C》阅读笔记(上)
目录 高质量iOS之熟悉OC 了解OC语言的起源 在类的头文件中尽量少引入其他头文件 多用字面语法,少用与之等价的方法 字面数值 字面量数组 字面量字典 局限性 多用类型常量,少用#define预处理指令 用枚举表示状态、选项、状态码 高质量iOS之对象…...

ClkLog里程碑:荣获2024上海开源技术应用创新竞赛三等奖
2024年10月,ClkLog团队参加了由上海计算机软件技术开发中心、上海开源信息技术协会联合承办的2024上海数智融合“智慧工匠”选树、“领军先锋”评选活动——开源技术应用创新竞赛。我们不仅成功晋级决赛,还荣获了三等奖!这一成就不仅是对ClkL…...

【数据结构进阶】哈希表
🌟🌟作者主页:ephemerals__ 🌟🌟所属专栏:数据结构 目录 前言 一、哈希表的概念 二、哈希函数的实现方法 1. 直接定址法 2. 除留余数法 三、哈希冲突 1. 开放定址法(闭散列࿰…...

STM32内存五区及堆栈空间大小设置(启动文件浅析)
前言 嘿,朋友们!今天咱们来聊聊STM32的内存五区和堆栈空间大小设置。这可是嵌入式开发里的“必修课”,要是没整明白,程序说不定就“翻车”了。别担心,我这就带你一步步搞懂这事儿,让你轻松上手,…...

微信小程序调用火山方舟(字节跳动火山引擎)中的DeepSeek大模型
微信小程序的轻量化特性与DeepSeek大模型的AI能力结合,可快速构建智能问答、内容生成等场景化服务。通过火山方舟平台提供的标准化接口,开发者无需深入算法细节即可调用模型能力。 一、注册火山引擎账号,创建API Key和model(接入…...

(八)Java-Collection
一、Collection接口 1.特点 Collection实现子类可以存放多个元素,每个元素可以是Object; 有些Collection的实现类,可以存放重复的元素,有些不可以; 有些Collection的实现类,有些是有序的(Li…...

从单片机的启动说起一个单片机到点灯发生了什么下——使用GPIO点一个灯
目录 前言 HAL库对GPIO的抽象 核心分析:HAL_GPIO_Init 前言 我们终于到达了熟悉的地方,对GPIO的初始化。经过漫长的铺垫,我们终于历经千辛万苦,来到了这里。关于GPIO的八种模式等更加详细的细节,由于只是点个灯&am…...

C++ | 哈希表
前言 💓 个人主页:普通young man-CSDN博客 ⏩ 文章专栏:C_普通young man的博客-CSDN博客 ⏩ 本人giee: 普通小青年 (pu-tong-young-man) - Gitee.com 若有问题 评论区见📝 🎉欢迎大家点赞👍收藏⭐文章 —…...
leetcode_动态规划/递归 70. 爬楼梯
70. 爬楼梯 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 思路: 考虑: 假设现在已经爬到了某一阶台阶,那是如何到达这里的呢?可能是从前一阶台阶爬上来的&am…...

基于Rook的Ceph云原生存储部署与实践指南(上)
#作者:任少近 文章目录 1 Ceph环境准备2 rook部署ceph群集2.1 Rook 帮助地址2.2 安装ceph2.3 获取csi镜像2.4 Master参加到osd2.5 设置默认存储 3 Rook部署云原生RBD块存储3.1 部署storageclass资源3.2 部署WordPress使用RBD3.3 WordPress访问 4 Rook部署云原生RGW…...
C++ Qt常见面试题(4):Qt事件过滤器
在 Qt 中,事件过滤器(Event Filter)提供了一种机制,可以拦截并处理对象的事件(如鼠标事件、键盘事件等),在事件到达目标对象之前对其进行预处理。事件过滤器通常用于以下场景: 捕获和处理特定的事件(如鼠标点击、按键等);对事件进行筛选或修改;实现全局的事件监听功…...
regionserver实例僵住问题分析
问题现象: 应用提交超时,发现regionserver实例异常。hbase原生页面这个实例dead,业务连接到这个rs的进程超时8个regionserver实例。 D08在18:30分后显示warning,应用提交任务到这个rs节点超时,hbase控制台不显示d08的rs信息了。19:30在页面停止rs实例失败,然后kill进程…...

服务器离线部署DeepSeek
目标 本次部署的目标是在本地服务器上部署DeepSeek。但是该服务不能连接外网,因此只能使用离线部署的方式。为了一次完成部署。现在云服务器上进行尝试。 云服务器部署尝试 云服务器配置 CentOS72080Ti 11GB 安装准备 1、上传iso并配置为本地yum源 安装前先将…...
QT mac系统下qml实现的菜单栏,标准快捷键Delete无作用或失灵的处理
一.在mac系统下,QT官方提供的删除快捷键无作用。 1.下面这一段代码,最后一个menuItem采用的是QT自带的标准快捷键,但是在使用的过程中,快捷键无响应。大家可以将代码带入简单的demo尝试一下: MenuBar {id: rootMenu {id: editMenutitle: TransText.titleBar_EditMenuItem…...
redis序列化设置
redis序列化设置 redis序列化设置序列化对象里有org.joda.time.DateTime1)、报错内容如下2)、解决方案:分别自定义时间的序列化和反序列化,以对象形式关联到redisTemplate redis序列化设置 redis序列化设置,通过自定义…...

浅谈C++/C命名冲突
前言 在这里我会简要地介绍产生命名冲突的原因,和C中处理命名冲突的方法,同时和C语言的解决办法进行比较。 相信你在阅读完之后一定会有收获。对于我们来说,了解编译器的编译链接过程才能更好的理解编译器是如何报错的,更能让我们…...

【语音编解码】常用的基于神经网络的语音编解码方案对比
引言 随着实时通信与多媒体应用的爆炸式增长,传统语音编解码技术正面临带宽效率与音质保真的双重挑战。近年来,基于深度学习的神经编解码器突破性地将端到端架构、动态码率控制与可解释信号处理相结合,在3kbps以下超低码率场景仍能保持自然语…...
PVE 配置显卡直通
博客地址:PVE 配置显卡直通 配置 Device: Dell PowerEdge T630CPU: Intel Xeon E5-2696 v4 x2GPU 1: Matrox Electronics Systems Ltd. G200eR2GPU 2: NVIDIA GeForce GTX 1060 3GBOS: Proxmox VE bookworm 8.3.1 x86_64 注意事项 硬件需支持并在 BIOS 中开启 I…...
Kronecker分解(K-FAC):让自然梯度在深度学习中飞起来
Kronecker分解(K-FAC):让自然梯度在深度学习中飞起来 在深度学习的优化中,自然梯度下降(Natural Gradient Descent)是一个强大的工具,它利用Fisher信息矩阵(FIM)调整梯度…...

ArcGIS Pro技巧实战:高效矢量化天地图地表覆盖图
在地理信息系统(GIS)领域,地表覆盖图的矢量化是一项至关重要的任务。天地图作为中国国家级的地理信息服务平台,提供了丰富且详尽的地表覆盖数据。然而,这些数据通常以栅格格式存在,不利于进行空间分析和数据…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...

云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...