[Effective Objective] 熟悉Objective-C

news2024/12/23 14:30:11

了解 Objective-C

Objective_C 是一种面向对象的语言。但与jave、C++等语言不同,它使用了消息结构(messaging structure)而非函数调用(function calling)。Objective-C由Smalltalk演化而来,后者是消息语言的鼻祖。

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

// Messaging (Objective-C)
Object* obj = [Object new];
[obj performWith:parameterl and:parameter2];

// Function calling (C++)
Object* obj = new Object;
obj->perform(parameter1, parameter2);

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

运行期组件(runtime component)

Objective-C的重要工作都由运行期组件(runtime component)而非编译器来完成。使用Objective-C的面向对象特性所需的全部数据结构及函数都在运行期组件里面。

运行期组件本质上就是一种与开发者所编代码相链接的“动态库”(dynamic library),其代码能把开发者编写的所有程序粘合起来。这样只需更新运行期组件,即可提升应用程序性能。

Objective-C内存模型

若要理解内存模型,则需明白:Objectivec-C语言中的指针是用来指示对象的。想要声明一个变量,令其指代某个对象,可用以下语法:

NSString *someString = @"The string";

它声明了一个名为someString的变量,其类型是NSString*。即此变量是指向NSString的指针。所有Objective-C语言的对象都必须这样声明,因为对象所占内存总是分配在“堆空间”(heap space)中,而绝不会分配在“栈”(stack)上。

someString变量指向分配在堆里的某块内存,其中含有一个NSString对象。也就是说,如果再创建一个变量,令其指向同一地址,那么并不拷贝该对象,只是这两个对象会同时指向此对象:

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

此时由两个NSString*型变量指向一个NSString实例。如下图:
在这里插入图片描述

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

Objective-C运行期环境吧堆内存管理工作抽象为一套内存管理结构,名叫“引用计数”。

结构体

与创建对象相比,创建结构体可以减少许多开销,例如分配及释放堆内存等。如果只需保存int、float、double、char等“非对象类型”,那么通常使用结构体就可以了。

例如CGRect,其定义是:

struct CGRect {
	CGPoint origin;
	CGSize size;
};
typedef struct CGRect  CGRect;

要点

  • Objective-C为C语言添加了面向对象特性,是其超急。Objective-C使用动态绑定的消息结构,也就是说,在运行时才会检查对象类型。接收一条消息之后,究竟应执行何种代码,由运行期环境而非编译器来决定。
  • 理解C语言的核心概念有助于写好Objective-C程序。尤其要掌握内存模型与指针。

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

与C和C++一样,Objective-C也使用“头文件”(header file)与“实现文件”(implementation file)来区隔代码。用Objective-C语言编写“类”的标准方式为:以类名做文件名,分别创建两个文件,头文件后缀用.h,实现文件后缀用.m。创建好一个类之后,其代码看上去如下所示:

// EOCPerson.h
#import <Foundation/Foundation.h>

@interface EOCPerson : NSObject
@property (nonattomic, copy) NSString *firstName;
@property (nonattomic, copy) NSString *lastName;
@end

//EOCPerson.m
#import "EOCPerson.h"

@implementation EOCPerson
// Implementation of methods
@end

向前声明

如果又创建一个名为EOCEmployer的新类,然后为EOCPerson类添加这个属性。

// EOCPerson.h
#import <Foundation/Foundation.h>

@interface EOCPerson : NSObject
@property (nonattomic, copy) NSString *firstName;
@property (nonattomic, copy) NSString *lastName;
@property (nonatomic, strong) EOCEmployer *employer;
@end

此时比起在EOCPerson.h下加入下面这行:

#import "EOCEmployer.h"

更推荐使用下面方法:

@class EOCEmployer;

这个方法叫做“向前声明”(forward declaring)该类。现在EOCPerson的头文件变成了这样:

// EOCPerson.h
#import <Foundation/Foundation.h>

@class EOCEmployer;

@interface EOCPerson : NSObject
@property (nonattomic, copy) NSString *firstName;
@property (nonattomic, copy) NSString *lastName;
@property (nonatomic, strong) EOCEmployer *employer;
@end

之所以使用向前声明,是因为在编译一个使用了EOCPerson类的文件时,不需要知道EOCEmployer类的全部细节,只需要知道有一个类名叫EOCEmploer就好。

EOCPerson类的实现文件则需引入EOCEmployer类的头文件,因为若要使用后者,则必须知道其所有接口细节。于是,实现文件就是:

//EOCPerson.m
#import "EOCPerson.h"
#import "EOCEmployer.h"

@implementation EOCPerson
// Implementation of methods
@end

尽量将引入头文件的时机延后,只在确有需要时才引入,这样就可以减少类的使用者所需引入头文件数量,减少编译时间。

相互引用

向前声明也解决了两个类相互引用的问题。

当编译器解析一个头文件时,编译器会发现它引入了另一个头文件,而那个头文件又回过头来引用第一个头文件,就会导致“循环引用”(chicken-and-egg situation)。使用#import而非#include指令虽然不会导致死循环,但这却意味着两个类里有一个无法被正确编译。

必须引入头文件

但是有时候必须要在头文件中引入其他头文件。如果你写的类继承自某个超类,则必须引入定义那个超类的头文件。同理,如果要声明你写的类尊从某个协议(protocol),那么该协议必须有完整定义,且不能使用向前声明。

例如,要从图形类中继承一个矩形类,且令其遵循绘制协议:

// EOCRectangle.h

#import "EOCShape.h"
#import "EOCDrawable.h"

@interface EOCRectangle : EOCShape<EOCDrawable>
@property (nonatomic, assign) float width;
@property (nonatomic, assign) float height;
@end

此时第二条#import是难免的。鉴于此,最好是把协议单独放在一个头文件中,以避免相互依赖问题。

然而有些协议,例如“委托协议”,就不用单独写一个头文件了。在那种情况下,协议之一与接收协议委托的类放在一起定义才有意义。此时,我们可以把在实现文件中声明此类实现了该委托协议,并把实现代码放在“class-continuation 分类”里。这样的话,只要在实现文件中引入包含委托协议的头文件即可,而不需要将其放在公共头文件里。

要点

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

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

字符串字面量

不使用alloc及init方法来分配并初始化NSString对象,让语法更简洁。

NSString *someString = @"Effective Objective-C 2.0";

这种语法也可以来声明NSNumber、NSArray、NSDictionary类的实例。

字面数值

NSNumber *intNumber = @1;
NSNumber *floatNumber = @2.5f;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';

字面量数组

字面量语法创建数组

NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];

字面量语法操作数组

NSString *dog = animals[1];

字面量字典

“字典”是一种映射型数据结构,可向其中添加键值对。

字面量字典创建

NSDictionary *personData = @{@"firstName" : @"Matt", @"lastName" : @"Galloway", @"age" : @28};

字面量语法访问

NSString *lastName = personData[@"lastName"];

这样写省去了沉赘的语法,令此行代码简单易读。

局限性

字面量语法除了字符串以外,所创建出来的对象必须属于Foundation框架才行。然而一般来说,标准的实现已经很好了,使用这些已经足够了。

此外,使用字面量语法创建出来的字符串、数组、字典对象都是不可变的(immutable)。若想要可变版本的对象,,则需复制一份:

NSMutableArray *mutable = [@[@1, @2, @3, @4] mutableCopy];

这样做会多调用一个方法,而且还要再创建一个对象,不过使用字面量语法所带来的好处还是多与上述缺点的。

要点

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

React 学习笔记总结(六)

文章目录1. redux 介绍2. redux 工作流程3. redux 的使用4. redux 完整结构补充5. redux的 异步action6. react-redux库 与 redux库7. react-redux库的 实战8. react-redux的connect 最精简写法9. react-redux的 Provider组件作用10. react-redux 整合UI组件 和 容器组件11. re…

webgl图形平移、缩放、旋转

文章目录前言平移图示代码示例缩放图示代码示例旋转公式推导代码示例总结前言 在webgl中将图形进行平移、旋转、缩放的操作称为变换或仿射变换&#xff0c;图形的仿射变换涉及到顶点位置的修改&#xff0c;通过顶点着色器是比较直接的方式。本文通过着色器实现对webgl图形的仿…

ArcGIS基础实验操作100例--实验65按字段调整点符号方向

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验65 按字段调整点符号方向 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff0…

计算机组成原理_总线

计算机组成原理总目录总线概述 1. 总线介绍 我们知道计算机中有CPU、主存、辅存&#xff0c;以及打印机、键盘、鼠标等等的一些外设 那么各个设备之间肯定是要进行数据传输的&#xff0c;这就需要许多线路将它们连接起来 第一种方法&#xff1a;两两相联 外设数量越多&#xf…

35、基于STM32的电子钟(DS1302)

编号&#xff1a;35 基于STM32的电子钟&#xff08;DS1302&#xff09; 功能描述&#xff1a; 本设计由STM32单片机液晶1602按键DS1302时钟组成。 1、采用STM32F103最小系统。 2、利用DS1302芯片提供时钟信号 3、液晶1602实时显示年月日、时分秒、星期等信息。 4、三个按键可…

隐形AR眼镜厂商Mojo Vision裁员75%,专注Micro LED技术

1月7日青亭网报道&#xff0c;隐形AR眼镜厂商Mojo Vision官方宣布了一项重大调整&#xff0c;其中因为产品进展问题&#xff0c;同时还有融资进展受阻等面临大裁员&#xff0c;将进行一系列中心调整&#xff0c;据了解本次裁员比例高达75%。重点关注&#xff1a;1&#xff0c;M…

【Day5】力扣第328题,奇偶链表

前言&#xff1a; 大家好&#xff0c;我是良辰丫&#x1f680;&#x1f680;&#x1f680;&#xff0c;今天带大家刷一个力扣链表题&#xff0c;有人可能会说&#xff0c;一道题够嘛&#xff0c;刚开始刷题别着急&#xff0c;毕竟&#xff0c;心急吃不了热豆腐&#xff0c;&…

Mathorcup数学建模竞赛第六届-【妈妈杯】B题:小区车位分布的评价和优化模型(附特等奖获奖论文和Java代码)

赛题描述 随着现代社会经济的快速发展,房地产成为国家经济发展中重要的经济增长点之一。而小区内汽车停车位的分布对于小区居民的上下班出行影响很大。请建立数学模型,解决下列问题: 问题1:分析评判小区汽车停车位分布是否合理的几个关键指标,建立评判车位分布合理的数学…

嵌入式Linux-对子进程的监控

1. 进程的诞生与终止 1.1 进程的诞生 一个进程可以通过 fork()或 vfork()等系统调用创建一个子进程&#xff0c;一个新的进程就此诞生&#xff01;事实上&#xff0c;Linux系统下的所有进程都是由其父进程创建而来&#xff0c;譬如在 shell 终端通过命令的方式执行一个程序./…

leetcode 1658. 将 x 减到 0 的最小操作数[python3 双指针实现与思路整理]

题目 给你一个整数数组 nums 和一个整数 x 。每一次操作时&#xff0c;你应当移除数组 nums 最左边或最右边的元素&#xff0c;然后从 x 中减去该元素的值。请注意&#xff0c;需要 修改 数组以供接下来的操作使用。 如果可以将 x 恰好 减到 0 &#xff0c;返回 最小操作数 &a…

HTML与CSS基础(四)—— CSS基础(选择器进阶、Emmet语法、背景属性、元素显示模式、三大特性)

一、选择器进阶目标&#xff1a;能够理解 复合选择器 的规则&#xff0c;并使用 复合选择器 在 HTML 中选择元素1. 复合选择器1.1 后代选择器&#xff1a;空格作用&#xff1a;根据 HTML 标签的嵌套关系&#xff0c;选择父元素 后代中 满足条件的元素 选择器语法&#xff1a;选…

第二章JavaWeb基础学习路线

文章目录什么是Java WebJava Web基础的技术栈关于我们的客户端与服务端&#xff08;BS&#xff09;我们客户端的形式**PC端网页****移动端**服务端应用程序关于请求&#xff08;request&#xff09;和响应(response)类比生活中的请求和响应服务器中的请求和响应项目的逻辑构成架…

CSS权威指南(六)文字属性

1.缩进和行内对齐 &#xff08;1&#xff09;缩进文本&#xff08;text-indent&#xff09; text-indent属性把元素的第一行文本缩进指定的长度&#xff0c;缩进的长度可以可以是负值。这个属性通常用于缩进段落的第一行。text-indent作用于块级元素之上&#xff0c;缩进将沿着…

config:配置中心

Spring Cloud Config 为分布式系统中的外部配置提供服务器端和客户端支持。使用 Config Server&#xff0c;您可以集中管理所有环境中应用程序的外部配置。 Spring Cloud Config就是一个配置中心&#xff0c;所有的服务都可以从配置中心取出配置&#xff0c;而配置中心又可以从…

mmap(内存映射)、sendfile() 与零拷贝技术

内存映射&#xff08;Memory-mapped I/O&#xff09;是将磁盘文件的数据映射到内存&#xff0c;用户通过修改内存就能修改磁盘文件。 RocketMQ为什么快&#xff1f;kafka为什么快&#xff1f;什么是mmap&#xff1f;这些问题都逃不过一个点&#xff0c;就是零拷贝。 虽然还有其…

电脑不能开机的几个常见原因

现在手机已经将电脑取代了&#xff0c;用电脑的越来越少&#xff0c;因为一些原因上网课的多了起来&#xff0c;大家都将放置几年的电脑搬了出来&#xff0c;开不开机的大有人在&#xff0c;由于机器闲置很久大多都出现了各种各样的故障和问题&#xff0c;在这里总结了电脑台式…

C语言:浮点型存储方式

浮点型存储方式 任意一个二进制浮点数V可以表示成下面的形式 (-1)^S *M *2^E 1&#xff08;S符号位&#xff09; 8&#xff08;E阶码&#xff09; 23&#xff08;M尾码&#xff09;省略首位1 S&#xff1a;表示正负 只有0/1两个值 M&#xff1a;由浮点数转化成二进制数表示 在…

4.7、IPv4 数据报的首部格式

固定部分&#xff1a;每个 IP 数据报首部都必须包含的内容 某些 IP 数据报的首部除了包含 202020 字节的固定部分外&#xff0c;还包含一些可选的字段来增加 IP 数据报的功能 IP 数据报的首部常以 323232 个比特为单位进行描述 图中的每一行都由 323232 个比特&#xff08;也…

小波分析——4.使用小波对信号成分进行分析

文章目录首先创建一个包含多频率成分的信号然后我们用数学实现一个墨西哥草帽小波然后我们开始对原始信号进行处理吧接下来可以把信号成分进行绘制在前面的章节里已经介绍过小波的理论、公式等知识点&#xff0c;现在我们来看看如何用小波来实现对复杂信号的成分分析。 在我们…

性能优化系列之『HTML5 离线化:主流的技术实现方案有哪些?』

文章の目录一、离线包类型二、离线包架构三、离线包下载四、离线包运行模式五、大厂离线包方案写在最后一、离线包类型 全局离线包&#xff1a;包含公共的资源&#xff0c;可供多个应用共同使用私有离线包&#xff1a;只可以被某个应用单独使用 二、离线包架构 三、离线包下载…