【iOS】《Effective Objective-C 2.0》阅读笔记(一)

文章目录

  • 前言
    • 了解OC语言的起源
    • 在类的头文件中尽量少引入其他头文件
    • 多用字面量语法,少用与之等价的方法
      • 字面量数值
      • 字面量数组
      • 字面量字典
    • 多用类型常量,少用#define预处理指令
    • 用枚举法表示状态、选项、状态码
  • 总结

前言

  最近开始阅读一些iOS开发的相关书籍,第一本就是《Effective Objective-C 2.0》,这里对第一周的阅读内容进行简单归纳和总结,主要是熟悉OC语言。

了解OC语言的起源

  OC语言由Smalltalk演化而来,其使用“消息结构”而非“函数调用”。

Smalltalk 起源于 20 世纪 70 年代的施乐帕洛阿尔托研究中心(Xerox PARC)。它是在当时计算机科学研究蓬勃发展的背景下诞生的,是面向对象编程(OOP)领域的先驱语言。
早期的 Smalltalk 主要用于研究和实验先进的编程概念。图构建一种能够模拟人类认知和交互方式的编程语言。例如,在当时传统的编程语言以过程式编程为主流时,Smalltalk 独树一帜地强调对象之间的消息传递。

对象是核心
在 Smalltalk 中,所有的实体都是对象。无论是简单的数据类型,如整数、字符、布尔值,还是复杂的用户自定义的数据结构,都被视为对象。例如,一个简单的整数对象 “5”,它不仅包含了数值本身,还包含了一系列可以对这个数值进行操作的方法。这些方法以消息传递的方式被调用,就好像这个整数对象能够 “理解” 并执行某些指令一样。
这种对象的概念是非常彻底的。以字符串对象为例,一个字符串对象可以接收诸如 “长度计算”“字符提取”“拼接其他字符串” 等消息,通过这些消息的传递来实现对字符串的各种操作。

消息传递机制
消息传递是 Smalltalk 中对象之间交互的主要方式。当一个对象需要另一个对象执行某个操作时,它会向对方发送一个消息。例如,假设有一个表示图形的对象和一个表示绘图工具的对象。图形对象可能会向绘图工具对象发送一个 “绘制我” 的消息,绘图工具对象收到消息后,会根据图形对象的属性(如形状、颜色等)来执行绘制操作。
消息可以带参数,也可以不带参数。比如,一个数字对象向另一个数字对象发送 “加法” 消息时,会带上要相加的数值作为参数。这种机制类似于人类之间的交流,一个对象 “请求” 另一个对象做某事,使得代码的逻辑更加符合自然的思维方式。

类与继承
类是创建对象的模板。在 Smalltalk 中,类定义了对象的属性和行为。例如,定义一个 “动物” 类,它可能包含 “名称”“年龄” 等属性,以及 “移动”“发出声音” 等行为(通过方法定义)。
继承是 Smalltalk 中实现代码复用和层次化设计的重要手段。例如,定义一个 “哺乳动物” 类继承自 “动物” 类,“哺乳动物” 类可以继承 “动物” 类的所有属性和行为,同时还可以添加自己特有的属性和行为,如 “喂奶” 等行为。这使得代码的组织结构更加清晰,符合现实世界中生物分类的逻辑。

对于消息和函数调用的区别,可以通过如下代码来体现:

//Messaging(OC)——"消息结构"
Object *obj = [Object new];        
[obj performWith:parameter1 and:parameter2];//Function calling(C++)——“函数调用“
Object *obj = new Object;
obj->perform(parameter1, parameter2);

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

OC的重要工作都由“运行期组建”而非编译器来完成。OC是C的“超集”,所以C语言中的所有功能在编写OC时仍然适用 。
OC语言中的指针是用来指示对象的。想要声明一个变量。令其指代某个对象,可用如下语法:

NSString *someString = @"The string";

上述代码声明了一个名为someString的变量,其类型为NSString*,即此变量为指向NSString的指针。所有的OC语言的对象声明必须以指针的形式,因为对象所占内存总是分配在“堆空间(heap space)”中,而绝不会在“栈(stack)”上
在这里插入图片描述
someString变量指向分配在堆里的某块内存,其中含有一个NSString对象,若再创建一个变量,令其指向同一地址,如下:

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

则并不会拷贝该对象,只是两个变量同时指向一个对象。即只有一个NSString实例,但有两个变量指向此实例,两个变量都是NSString类型。如图:
在这里插入图片描述
但是,在OC中有时会遇到定义里不带有
的变量,它们可能使用到“栈空间”,这些变量保存的不是CO对象。例如:

CGRect frame;
frame.origin.x = 0.0f;
frame.origin.y = 10.0f;
frame.size.width = 100.0f;
frame.size.height = 150.0f;.

小结

  • OC为C语言添加了面向对象特征,是其的超集。OC使用动态绑定的消息结构,即在运行时才会检查对象类型。接受一条消息后,究竟应执行什么代码,有运行环境而非编译器来决定。
  • 理解C语言的核心概念有助于写好OC程序。尤其要掌握内存模型和指针。

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

  假设我们有两个类。Person类可能会拥有一些Book类的对象,作为他所拥有的藏书。

Book.h头文件:

#import <Foundation/Foundation.h>@interface Book : NSObject
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *author;@end

  在Person.h头文件中,假设我们只是想声明一个Book的指针作为成员变量(表示这个人拥有的一本书),我们如果如下在Person.h文件中直接引入Book.h文件,就在两者之间建立了一种依赖关系。
  如果过多地引入头文件,会导致代码的耦合性增加,还可能引入许多根本用不到的内容,会增加代码的编译时间和维护成本。

耦合性是指不同模块(在这里可以理解为不同的类)之间相互依赖的程度。例如,如果类 A 的头文件中引入了许多其他类的头文件,那么当这些被引入头文件中的类发生变化(如修改了成员变量或者方法签名)时,类 A 可能也需要进行相应的修改,这就增加了维护成本。

Person.h头文件:

#import <Foundation/Foundation.h>
#import "Book.h"@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) Book *favoriteBook;@end

为了避免上述情况,在Person.h头文件中,如果我们只是想声明一个Book的指针作为成员变量(表示这个人拥有的一本书),我们可以使用向前声明来避免引入Book.h头文件,如下:

Person.h头文件:

#import <Foundation/Foundation.h>
@class Book;@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) Book *favoriteBook;@end

然后在Person.m实现文件中,当我们需要真正使用Book类的完整定义(例如访问Book的属性或者调用Book的方法)时,再引入Book.h头文件:

Person.m文件:

#import "Person.h"
#import "Book.h"@implementation Person
// 这里可以使用Book类的完整定义来实现Person类的方法
// 比如设置favoriteBook的属性等操作
@end

向前引用:

  • 解决了头文件循环引用的问题

在复杂的代码结构中,尤其是涉及多个类或模块相互关联时,很容易出现头文件循环包含的情况。例如,类 A 的定义可能依赖于类 B 的部分信息,而类 B 的定义又依赖于类 A 的部分信息。如果直接使用头文件包含的方式,编译器会陷入无限循环地处理这些相互包含的头文件,造成循环引用。虽然我们使用#import而非#include不会导致死循环,但是这意味着两个类中有一个类无法被正确编译

  • 但当涉及协议(Protocol)相关操作时,进行向前声明就无法满足需求了。在 Objective - C 中,当一个类声明遵循某个协议(Protocol)时,编译器需要知道协议的完整定义来检查该类是否正确地实现了协议中的方法。这时就必须要引入头文件,而不能使用向前声明了。

小结

  • 除非确有必要,否则不要引入头文件。一般来说,应在某个类的头文件中使用向前声明来提及别的类(使用@class),并在实现文件中引入那些类的头文件(使用import)。这样做可以尽量降低类之间的耦合。
  • 有时无法使用向前声明,比如要声明某个类遵循一项协议。这种情况下,尽量把“该类遵循某协议”的这条声明移至分类中。如果不行的话,就把协议单独放在一个头文件中,然后将其引入。

多用字面量语法,少用与之等价的方法

//字面量语法
NSString *someString = @"Effective Objective-C";
//与之等价的创建方法
NSString *someString = [[NSString alloc] initWithString:@"Effective Objective-C"];

字面量数值

//字面数值
NSNumber *someNumber = @1;
//与之等价的数值创建方法
NSNumber *someNumber = [NSNumber numberWithInt:1];

能够以NSNumber实例表示的所有数据都可以使用该语法:

NSNumber *intNumber = @1;
NSNumber *floatNumber = @2.5f;
NSNumber *doubleNumber = @3.14159;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';//字面量语法对于以下表达式也适用
int x = 5;
float y = 6.32f;
NSNumber *expressionnumber = @(x * y);

字面量数组

//字面量语法
NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];
NSString *dog = animals[1];
//与之等价的方法
NSArray *animals = [NSArray arrayWithObjects:@"cat", @"dog", @"mouse", @"badger", nil];
NSString *dog = [animals objectAtIndex:1];

但当你使用字面量语法创建数组时,数组元素对象中不能有nil,否则会报错。
在这里插入图片描述

因为在 Objective - C 中,使用字面量语法创建NSArray时,nil是一个特殊的值,它用于表示数组元素的结束标记,不能作为一个普通元素放在数组中间或者末尾。如果像这样把nil当作普通元素添加进去,会导致运行时错误,程序很可能会崩溃。因为系统在解析这个字面量创建的数组时,一旦遇到nil就会认为数组元素已经结束了,后面的内容不会再被当作数组元素处理了。在使用字面量语法创建字典时同理。

字面量字典

//字面量语法创建字典
NSDictionary *myDictionary = @{@"key1" : @"value1", @"key2" : @"value2", @"key3" : @"value3"};
//与之等价的方法
NSArray *keys = @[@"key1", @[@"key2", @"key3"]];
NSArray *objects = @[@"value1", @"value2", @"value3"];
NSDictionary *myDictionary = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
}

字面量语法的局限性:
  字面量语法除字符串之外,所创建出来的对象必须属于Foundation框架才行。如果定义了这些类的子类,就无法使用字面量语法创建其对象。字面量通常只能用于特定的数据类型,比如数组和字典。不能使用字面量语法来创建自定义类的实例。
小结

  • 应该使用字面量语法来创建字符串、数值、数组、字典。与创建此类对象的常规方法相比,这么做更加简明扼要。
  • 应该通过取下标操作来访问数组下标或字典中的键所对应的元素。
  • 用字面量语法创建数组或字典时,若值中有nil,则会抛出异常。因此,务必确保值里不含nil。

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

定义常量的命名

#define MAX_NUMBER 22

在实际的开发里面,这样定义出来的常量没有类型信息,并且假设此命令在某个头文件中,那么所有引入了这个头文件的的代码,其定义的固定值都会被这个替换掉。

类型常量的方法:

static const NSInteger kNumber = 22;

定义常量的位置方法

定义常量的位置是极其重要的,我们总喜欢在头文件里声明预处理指令,那么引入了这个头文件的所有文件都会含有这个变量,万一重名,程序变得异常麻烦。所以最好不要在头文件中定义常量,不论你是如何定义常量的,因为OC中没有“名称空间”这一概念。

因此我们最好在头文件中声明常量,在实现文件中定义常量
小结

  • 不要用预处理指令定义常量。这样定义出来的常量不含类型信息,编译器只是会在编译前据此执行查找与替换操作。即使有人重新定义了常量值,编译器也不会产生警告信息,这将导致应用程序中的常量值不一致。
  • 在实现文件中使用static const来定义“只在编译单元内可见的常量”(translation-unit-specificconstant)。由于此类常量不在全局符号表中,所以无须为其名称加前缀。
  • 在头文件中使用extern来声明全局常量,并在相关实现文件中定义其值。这种常量要出现在全局符号表中,所以其名称应加以区隔,通常用与之相关的类名做前缀。

用枚举法表示状态、选项、状态码

  • 应该用枚举来表示状态机的状态、传递给方法的选项以及状态码等值,给这些值起个易懂的名字。
  • 如果把传递给某个方法的选项表示为枚举类型,而多个选项又可同时使用,那么就将各选项值定义为2的幂,以便通过按位或操作将其组合起来。
  • 用NS_ENUM与NS_OPTIONS宏来定义枚举类型,并指明其底层数据类型。这样做可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型。
  • 在处理枚举类型的switch语句中不要实现default分支。这样的话,加入新枚举之后,编译器就会提示开发者:switch语句并未处理所有枚举。

总结

  从本周开始,笔者开始阅读Effective Objective-C 2.0,本周了解到OC的起源和部分代码编写时的优化,发现自己之前代码有很多不足,后续还会继续阅读这本书。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/482022.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

猫狗分类调试过程

一&#xff0c;下载名称为archive数据集 下载方式&#xff1a;机房共享文件夹 二、打开CatDogProject项目 配置环境&#xff1a;选择你所建的环境 三、调试运行 1&#xff0c;报错一&#xff1a;Traceback (most recent call last): File "G:/AI_Project/CatDogPro…

探索Python WebSocket新境界:picows库揭秘

文章目录 探索Python WebSocket新境界&#xff1a;picows库揭秘第一部分&#xff1a;背景介绍第二部分&#xff1a;picows库概述第三部分&#xff1a;安装picows库第四部分&#xff1a;简单库函数使用方法第五部分&#xff1a;场景应用第六部分&#xff1a;常见Bug及解决方案第…

零基础学安全--Burp Suite(4)proxy模块以及漏洞测试理论

目录 学习连接 一些思路 proxy模块 所在位置 功能简介 使用例子 抓包有一个很重要的点&#xff0c;就是我们可以看到一些在浏览器中看不到的传参点&#xff0c;传参点越多就意味着攻击面越广 学习连接 声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可…

30 基于51单片机的手环设计仿真

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于STC89C52单片机&#xff0c;DHT11温湿度采集温湿度&#xff0c;滑动变阻器连接ADC0832数模转换器模拟水位传感器检测水位&#xff0c;通过LCD1602显示信息&#xff0c;然后在程序里设置好是否…

十一、快速入门go语言之接口和反射

文章目录 接口:one: 接口基础:two: 接口类型断言和空接口:star2: 空接口实现存储不同数据类型的切片/数组:star2: 复制切片到空接口切片:star2: 类型断言 反射 &#x1f4c5; 2024年5月9日 &#x1f4e6; 使用版本为1.21.5 接口 十、Java类的封装和继承、多态 - 七点半的菜市…

QT6学习第六天 初识QML

QT6学习第六天 创建Qt Quick UI项目使用Qt Quick DesignerQML 语法基础导入语句 import对象 object 和属性 property布局注释表达式和属性绑定QML 编码约定 设置应用程序图标 创建Qt Quick UI项目 如果你有只测试QML相关内容快速显示界面的需求&#xff0c;这时可以创建Qt Qui…

图解RabbitMQ七种工作模式生产者消费者模型的补充

文章目录 1.消费者模型2.生产者-消费者模型注意事项2.1资源释放顺序问题2.2消费者的声明问题2.3虚拟机和用户的权限问题 3.七种工作模式3.1简单模式3.2工作模式3.3发布/订阅模式3.4路由模式3.5通配符模式3.6RPC通信3.7发布确认 1.消费者模型 之前学习的这个消息队列的快速上手…

C-操作符

操作符种类 在C语言中&#xff0c;操作符有以下几种&#xff1a; 算术操作符 移位操作符 位操作符 逻辑操作符 条件操作符 逗号表达式 下标引用&#xff0c;函数调用 拓展&#xff1a;整型提升 我们介绍常用的几个 算术操作符 &#xff08;加&#xff09;&#xff…

使用 Spring Boot 和 GraalVM 的原生镜像

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;历代文学&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编程&#xff0c;高并发设计&#xf…

基于Java Springboot宠物医院微信小程序

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 微信…

Tree搜索二叉树、map和set_数据结构

数据结构专栏 如烟花般绚烂却又稍纵即逝的个人主页 本章讲述数据结构中搜索二叉树与HashMap的学习&#xff0c;感谢大家的支持&#xff01;欢迎大家踊跃评论&#xff0c;感谢大佬们的支持! 目录 搜索二叉树的概念二叉树搜索模拟实现搜索二叉树查找搜索二叉树插入搜索二叉树删除…

C#使用ExcelDataReader读取Xlsx文件为DataTable对象

创建控制台项目 在NuGet中安装ExcelDataReader.DataSet 3.7.0 创建一个xlsx文件 测试代码 读取xlsx文件内容&#xff0c;为一个DataTable对象。 读取xlsx时&#xff0c;xlsx文件不能被其他软件打开&#xff0c;否则会报“进程无法访问此文件”的错。 using ExcelDataRead…

【JavaEE初阶】应是天仙狂醉,乱把白云揉碎 - (重点)线程

本篇博客给大家带来的是线程的知识点, 由于内容较多分几天来写. &#x1f40e;文章专栏: JavaEE初阶 &#x1f680;若有问题 评论区见 ⭐欢迎大家点赞 评论 收藏 分享 ❤❤❤ 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 1. 认识线程 1.1 概念 )1 …

精准用户获取与私域流量运营:多商户链动 2+1 模式商城小程序的赋能策略

摘要&#xff1a;本文聚焦于精准用户对商业运营的核心价值&#xff0c;深入剖析获取精准用户的有效途径&#xff0c;特别围绕目标用户画像及出没场景展开分析。同时&#xff0c;探讨在私域流量构建进程中&#xff0c;多商户链动 21 模式商城小程序如何融入精准用户运营体系&…

Spring Boot教程之十一:获取Request 请求 和 Put请求

如何在 Spring Boot 中获取Request Body&#xff1f; Java 语言是所有编程语言中最流行的语言之一。使用 Java 编程语言有几个优点&#xff0c;无论是出于安全目的还是构建大型分发项目。使用 Java 的优点之一是 Java 试图借助类、继承、多态等概念将语言中的每个概念与现实世…

DVWA靶场文件包含(File Inclusion)通关教程(high级别)

目录 DVWA 靶场建立闯关 DVWA 靶场建立 需要的东西&#xff1a; phpStudy&#xff1a; 链接&#xff1a; phpStudy 提取码&#xff1a;0278 DVWA-master 链接&#xff1a; DVWA靶场 提取码&#xff1a;0278 建议在虚拟机中操作&#xff0c;以防数据库冲突&#xff0c;下面有…

基于yolov8、yolov5的铝材缺陷检测识别系统(含UI界面、训练好的模型、Python代码、数据集)

摘要&#xff1a;铝材缺陷检测在现代工业生产和质量管理中具有重要意义&#xff0c;不仅能帮助企业实时监控铝材质量&#xff0c;还为智能化生产系统提供了可靠的数据支撑。本文介绍了一款基于YOLOv8、YOLOv5等深度学习框架的铝材缺陷检测模型&#xff0c;该模型使用了大量包含…

力扣刷题TOP101:8.BM10 两个链表的第一个公共结点

目录&#xff1a; 目的 思路 复杂度 记忆秘诀 python代码 目的 两个无环的单向链表&#xff0c;它们的第一个公共结点{{6,7}。 思路 这个任务是找到两个链表的第一个公共结点。可以看作两个心机boy偷偷补课翻车事件。平时嘴上说自己在家玩游戏&#xff0c;实际上背地里都偷…

哪些行业对六西格玛管理方法的需求较大?

六西格玛作为一种追求极致质量和流程优化的管理哲学&#xff0c;自诞生以来&#xff0c;便在多个行业中展现出了巨大的应用价值。该方法通过定义、测量、分析、改进和控制&#xff08;DMAIC&#xff09;五个阶段&#xff0c;帮助企业实现流程的持续改进&#xff0c;提高产品质量…

Spring Web MVC其他扩展(详解下)

文章目录 Spring MVC其他扩展&#xff08;下&#xff09;异常处理异常处理机制声明式异常好处基于注解异常声明异常处理 拦截器拦截器概念拦截器使用拦截器作用位置图解拦截器案例拦截器工作原理源码 参数校验校验概述操作演示SpringMVC自定义参数验证ValueObject(VO) 文件上传…