Effective Objective-C 第一章阅读笔记

embedded/2025/1/14 18:46:00/

Effective Objective-C 第一章阅读笔记

文章目录

  • Effective Objective-C 第一章阅读笔记
    • OC的语言起源
      • 内存管理
      • 小结
    • 在类的头文件中尽量少引入其他头文件
      • 小结
    • 多用字面量语法
    • 多用类型变量,少用#define预处理指令
      • 小结
    • 枚举表示状态,选项,状态码

OC的语言起源

OC采用的使用消息结构,而不是函数调用。OC由Smalltalk演化而来,后者是消息型语言的鼻祖。

消息与函数调用之间的区别看上去就像这样:

// Messaging(Objective-C)
Object* obj = [Object new];
[obj performWith: parameter1 and: parameter2];// Function calling(C++)
Object* obj = new Object;
obj->perform(parameter1, parameter2);

这里的关键区别在于,消息结构的语言,其运行时所应执行的代码由运行环境来决定;使用函数调用的语言,则由编译器来决定

如果这里面调用的函数时多态的,那么对于函数型语言则是通过一个虚方法表来查出应该执行哪一个函数,至于采用消息结构的语言,无论是否多态,总是在运行时才会去查找所要执行的一个方法。

OC的重要工作都由运行期组件完成,运行期组件本质上是一种与开发者所连接的动态库,这里会在后面的内容中介绍。

内存管理

OC的有些语法是照搬C语言的,OC中的指针式用来指示对象的。

NSString *someString = @"The String"

上面代码中的someString变量是用来指代一个NSString的类型的,这里所有的对象都是存储到我们的堆空间中的,而不是栈区。也就是说这种代码是不可以存在的:

NSString someString;

下面的代码体现出了一个浅拷贝的内容。

NSString* someString = @"The string";
NSString* anotherString = someString;

下图描述了他的一个内存分布

image-20250111114240666

分配在堆中的内存必须直接管理,分配在栈上的用于保存变量的内存会在其栈帧弹出时自动清理

OC采用引用计数来管理堆区的内存,当然OC中也存在定义中不含有*的变量

image-20250111114610974

创建结构体相比,创建对象还需要额外开销,这里采用结构体可以节约很多内存。

小结

  • Objective-C为C语言添加了面向对象特性,是其超集。Objective-C使用动态绑定的消息结构,也就是说,在运行时才会检查对象类型。接收一条消息之后,究竟应执行何种代码,由运行期环境而非编译器来决定。

在类的头文件中尽量少引入其他头文件

在开发中我们经常会创建很多类,在OC中类的头文件是如下的结构:

// EOCPerson.h
#import <Foundation/Foundation.h>
@interface EOCPerson : NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@end
// EOCPerson.m
#import "EOCPerson.h"
@implementation EOCPerson
// Implementation of methods
@end

但一般我们可能会创建另一个类别EOCEmployer,后面发现每一个EOCEmployer都应该被EOCPerson持有,于是我们会在EOCPerson的头文件的前面添加一天#improt "EOCEmployer.h",尽管这个可以实现,但是浪费很多时间,在头文件部分我们其实只需要让EOCPerson知道这个类的存在就可以了,所以我们只用采用@class EOCEmployer就可以实现了

如果在EOCPerson的代码中我们需要用到EOCEmployer的接口细节的时候,我们在类的实现部分在导入对应的头文件#improt "EOCEmployer.h

// EOCPerson.m
#import "EOCPerson.h"
#import "EOCEmployer.h"
@implementation EOCPerson
// Implementation of methods
@end

同时向前声明还可以解决我们这里的两个类互相引用的一个问题

image-20250111140737380

image-20250111140803799

这里可以看到我们在两个头文件中互相引用的时候,会出现编译错误的问题,但是如果我们换成@class就不会出现对应的一个问题

这里引用原文来介绍一下:

image-20250111141920282

但是我们在引入协议的时候,就不可以使用向前声明。向前声明只能告诉编译器有某个协议,而编译器却要知道他定义的方法。

但是在某些委托协议中,就不用单独写一个头文件了。在那种情况下,协议只有与接受协议委托的类在一起定义才有意义,可以把那段实现代码放在对应分类中。

小结

  • 除非确有必要,否则不要引入头文件,尽量采用向前声明来提及别的类
  • 无法使用向前声明的时候,尽量把’这个类遵循某协议’这一条声明放在分类中,

多用字面量语法

这部分内容主要还是一些更加便捷的语法:

比方说NSNumber可以采用

NSNumber* number = @1
NSNumber* number = @1.5f
NSNumber* number = @YES
NSNumber* number = @'a'
int x = 3
float y = 6.32f
NSNumber* = @(x * y)

NSArray可以采用

NSArray* ary = @[@"123", @"223", @"3214"];
//取对应的一个下标
ary[1]//字面量取法

NSDictionary可以采用

NSDictionary* data = [@"123":@"213"];
//取某一个值
data[@"123"];

可变数组与字典

mutableAry[1] = @"dog"
mutableDryp[@"213"] = @"23144"
  • 注意使用字面量语法的时候,我们要保证我们的值不会出现nil,如果出现nil会抛出异常。

多用类型变量,少用#define预处理指令

熟悉C语言的我们,对于下面这一行代码也是比较熟悉,但是这段代码其实会造成某些隐患

image-20250111191041275

假设这个指令声明在某个头文件中,那么所有引入了头文件的代码,他都会被替换。

当我们在完成一个很大的项目中,很可能会出现重复的常量名,这样可能会出现一个替换,从而导致代码出现一些问题。

我们应该把预处理指令换成我们的一个类型常量:

static const int kDuration = 30;

我们可以发现他有类型信息,可以更好的描述来常量的含义,这里我们命名常量的时候,如果局限在某一个编译单元中就加字母k,如果常量在类之外可见,则通常以类名为前缀。

然后我们尽量不要在头文件中定义,因为我们如果在头文件中声明的话,就相当于声明了一个名字为kDuration的全局变量,所以我们不应该在头文件中定义。

只要不打算公开这个常量,我们应该将其定义在该常量的实现文件中。变量一定要同时用static和const来声明

  • 采用static可以保证这个变量只能在定义此变量的编译单元中可见(也就是定义他的一个实现的.m文件)
  • 采用const可以保证他不被修改。

如果不采用static,这样如果在另一个实现文件中定义一个同名的变量会抛出一个错误。

这里其实采用static和const一起来修饰的话,编译器其实还是和#define一样的实现,但是他会有类型信息。

在某些情况下,我们可能需要给外部一个可见的常量,这类常量我们应该这样定义:

extern NSString* *const EOCString;
extern const int duration;
//头文件
NSString *const EOCString = @"Value"const int duration = 30;
//实现部分

这里我们要注意const修饰的位置,const是为了保证我们的这个指向EOCString的指针不变。

编译器看到我们在头文件中的extern就会把他添加到全局符号表中,因为符号会被添加到全局符号表中,所以命名常量需谨慎,千万不能出现名称冲突。

总之,我们呢不要使用预处理定义常量,应该借助编译器来保证我们呢的常量正确,在实现部分可以采用static和const。(这里笔者之后会写一篇有关这几个关键字的博客)

小结

  • 不要用预处理命令
  • 在实现文件中用static和const来定义在编译单元内可见的常量
  • 在头文件中用extern来声明全局变量,并在相关实现文件中定义他的值

枚举表示状态,选项,状态码

这里简单介绍一下他的语法。

enum EOCConnectionState {EOCConnectionStateDisconnected
};
typedef enum EOCConnectionState EOCConnectionState;
EOCConnectionState state = EOCConnectionStateDisconnected;

在C++11之后我们可以指定使用何种底层数据类型。

typedef enum : NSUInteger { //这里保证我们的一个底层数据类型是NSInteger<MyEnumValueA>,<MyEnumValueB>,<MyEnumValueC>,
} <MyEnum>;

我们也可以在某一个值开始,通过手工来指定某个枚举成员所对应的一个值。

enum EOCConnectionState {EOCConnectionStateDisconnected = 1,EOCConnectionStateConnected
};

image-20250111204229743

这里就可以通过按位与来组合不同的选项。

最后我们在switch语句中可以用枚举值来代替对应的一个选择。

image-20250111205023063

然后我们的switch中尽量不要使用default的分支。


http://www.ppmy.cn/embedded/153907.html

相关文章

CES Asia 2025:VR/AR/XR引领科技新潮流

在全球科技领域蓬勃发展的大背景下&#xff0c;CES Asia 2025&#xff08;赛逸展&#xff09;即将在京盛大开幕&#xff0c;VR/AR/XR技术作为前沿科技的代表&#xff0c;将在本次展会上大放异彩&#xff0c;展现出令人瞩目的发展趋势和巨大潜力&#xff0c;同时政策优势也将为其…

Spring底层核心原理解析

​ 本次分享会把Spring中核心知识点都给大家进行串讲&#xff0c;让大家对Spring的底层有一个整体的大致了解&#xff0c;比如&#xff1a; Bean的生命周期底层原理依赖注入底层原理初始化底层原理推断构造方法底层原理AOP底层原理Spring事务底层原理 但都只是大致流程&#…

docker简单使用

进入和退出docker linux命令 docker ps -a 查询已经创建的容器&#xff0c;包括正在运行的和已停止的容器docker start id-name 启动容器docker exec -it id-name /bin/bash 进入容器docker stop id-name 停止容器

说一说mongodb组合索引的匹配规则

一、背景 有一张1000多万条记录的大表&#xff0c;需要做归档至历史表&#xff0c;出现了大量慢查询。 查询条件是 "classroomId": {$in: ["xxx", "xxx", ..... "xxx","xxx", "xxx" ] }耗时近5秒&#xff0c;且…

2. Doris数据导入与导出

一. Doris数据导入 导入方式使用场景支持的文件格式导入模式Stream Load导入本地文件或者应用程序写入csv、json、parquet、orc同步Broker Load从对象存储、HDFS等导入csv、json、parquet、orc异步Routine Load从kakfa实时导入csv、json异步 1. Stream Load 基本原理 在使用…

英语互助小程序springboot+论文源码调试讲解

第2章 开发环境与技术 英语互助小程序的编码实现需要搭建一定的环境和使用相应的技术&#xff0c;接下来的内容就是对英语互助小程序用到的技术和工具进行介绍。 2.1 MYSQL数据库 本课题所开发的应用程序在数据操作方面是不可预知的&#xff0c;是经常变动的&#xff0c;没有…

No. 31 笔记 | Web安全-SQL手工注入技术学习 Part 2

一、研究背景 背景介绍 SQL注入是一种常见且高危的Web安全漏洞。攻击者可以通过构造恶意SQL查询语句来绕过验证机制&#xff0c;执行未授权操作&#xff0c;如获取敏感信息、篡改数据库内容甚至控制服务器。 研究内容 本笔记探讨以下数据库的手工注入技术&#xff1a; MySQLAc…

PostgreSQL 超级管理员详解

1. 什么是 PostgreSQL 超级管理员 PostgreSQL 超级管理员&#xff08;superuser&#xff09;是拥有数据库系统最高权限的用户。他们可以执行任何数据库操作&#xff0c;包括但不限于创建和删除数据库、用户、表空间、模式等。超级管理员权限是 PostgreSQL 中权限的最高级别。 …