《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)领域,地表覆盖图的矢量化是一项至关重要的任务。天地图作为中国国家级的地理信息服务平台,提供了丰富且详尽的地表覆盖数据。然而,这些数据通常以栅格格式存在,不利于进行空间分析和数据…...
不记命令也能排障:catpaw chat 实战手册俟
Julia(julialang.org)由Stefan Karpinski、Jeff Bezanson等在2009年创建,目标是融合Python的易用性、C的高性能、R的统计能力、Matlab的科学计算生态。 其核心设计哲学是: 高性能:编译型语言(JIT࿰…...
2026届必备的降AI率平台推荐
Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 当前,在学术研究这个范畴之内,借助人工智能技术来辅助论文撰写这种行…...
2026届毕业生推荐的六大AI辅助论文方案推荐榜单
Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 在学术论文撰写里,DeepSeek是智能写作辅助工具,它展现出显著效能。使…...
2026最全国内外电商API接口的数据列表与采集
电商数据采集 API 主要分为 官方开放平台 API(合规、稳定)和 第三方聚合 API(多平台统一、接入快)。下面按 国内主流平台(淘宝 / 天猫、京东、1688、拼多多) 跨境平台(亚马逊、速卖通、Shopee&a…...
出厂自带“缰绳”的AI来了,Hermes Agent正在重新定义智能体
在过去的一年里,AI Agent领域经历了一场又一场热潮,OpenClaw带来的“龙虾热”还没有完全散去,一款名为Hermes Agent的开源智能体框架又迅速闯入开发者视野。发布短短两个月,GitHub星标就突破27000,没有铺天盖地的营销&…...
3大核心优势+零门槛配置:Perseus开源工具助你畅享完整游戏体验
3大核心优势零门槛配置:Perseus开源工具助你畅享完整游戏体验 【免费下载链接】Perseus Azur Lane scripts patcher. 项目地址: https://gitcode.com/gh_mirrors/pers/Perseus 作为一款针对游戏体验优化的开源工具,Perseus凭借其独特的无偏移地址…...
Linux I/O 演进史:从管道到零拷贝,一篇串起个服务端核心原语俅
前言 在使用 kubectl get $KIND -o yaml 查看 k8s 资源时,输出结果中包含大量由集群自动生成的元数据(如 managedFields、resourceVersion、uid 等)。这些信息在实际复用 yaml 清单时需要手动清理,增加了额外的工作量。 使用 kube…...
谐振式与耦合式WPT系统中收发线圈的等效电路建模与性能对比
1. 无线能量传输的基本原理 想象一下,你正在给手机充电,但不需要插线,只要把手机放在桌面上就能自动充上电。这种看似科幻的场景,正是无线能量传输(WPT)技术带来的现实。作为从业十多年的工程师,我见证了这个领域从实验…...
英雄联盟智能助手ChampR:快速提升游戏水平的终极指南
英雄联盟智能助手ChampR:快速提升游戏水平的终极指南 【免费下载链接】champr 🐶 Yet another League of Legends helper 项目地址: https://gitcode.com/gh_mirrors/ch/champr 你是否在英雄联盟游戏中苦苦寻找最佳的出装和符文配置?C…...
tio 配置完全手册:从基础设置到高级配置档
tio 配置完全手册:从基础设置到高级配置档 【免费下载链接】tio A serial device I/O tool 项目地址: https://gitcode.com/gh_mirrors/ti/tio tio 是一款功能强大的串行设备 I/O 工具,能够帮助用户轻松管理和配置串行端口通信。本指南将从基础设…...
