【Effective Objective - C】—— 熟悉Objective-C

【Effective Objective - C】—— 熟悉Objective-C

  • 熟悉Objective-C
  • 1.oc的起源
    • 消息和函数的区别
    • 运行期组件和内存管理
    • 要点:
  • 2.在类的头文件中尽量少引入其他头文件
    • 向前声明
    • 要点:
  • 3.多使用字面量语法,少用与之等价的方法
    • 字符串字面量
    • 字面数值
    • 字面数组
    • 字面字典
    • 局限性
    • 要点
  • 4.多用类型常量,少用#define预处理指令
    • 常量的名称与位置
      • 常量命名法
      • 位置
    • 使用static与const来声明
      • static修饰符
      • const修饰符
    • 使用extern声明全局变量
    • 要点
  • 5.用枚举表示状态,选项,状态码
    • 要点

熟悉Objective-C

Objective-C通过一套全新语法,在C语言基础上添加了面向对象特性。Objective-C的语法中频繁使用方括号,而且不吝于写出极长的方法名,这通常令许多人觉得此语言较为冗长。其实这样写出来的代码十分易读,只是C++或Java程序员不太能适应。
Objective-C语言学起来很快,但有很多微妙细节需注意,而且还有许多容易为人所忽视的特性。另一方面,有些开发者并未完全理解或是容易滥用某些特性,导致写出来的代码难于维护且不易调试。本章讲解基础知识,后续各章谈论语言及其相关框架中的各个特定话题。

1.oc的起源

和C++,Java一样,Objective-C也是面向对象语言,但是它们在许多方面都有差别。差别在于Objective-C使用的是消息结构而非函数调用,Objective-C语言由Smalltalk演化而来的,Smalltalk是消息型语言的鼻祖

消息和函数的区别

//Messaging
Object* obj = [Object new];
[obj performWith: parameter1 and: parameter2];
//Function
Object* obj = new Object;
obj->preform (parameter1, parameter2);

关键区别在于:使用消息结构的语言,其运行时所应执行的代码由环境来决定;使用函数调用的语言,则由编译器决定。对于函数来说,如果范例代码的调用函数是多态的,那么就在运行时按照虚方法表来查出来到底执行哪个函数,而采用消息结构的语言,不管是否为多态总是在运行时才回去查找所要执行的方法。

运行期组件和内存管理

  • Objective-C的重要工作都由“运行期组件”(runtime component)而非编译器来完成。使用Objective-C的面向对象特性所需的全部数据结构及函数都在运行期组件里面。举例来说,运行期组件中含有全部内存管理方法。运行期组件本质上就是一种与开发者所编代码相链接的“动态库”(dynamic library),其代码能把开发者编写的所有程序粘合起来。这样的话,只需更新运行期组件,即可提升应用程序性能。而那种许多工作都在“编译期”(compile time)完成的语言,若想获得类似的性能提升,则要重新编译应用程序代码。
  • Objective-C是C的“超集”(superset),所以C语言中的所有功能在编写Objective-C代码时依然适用。因此,必须同时掌握C与Objective-C这两门语言的核心概念,方能写出高效的Objective-C代码来。其中尤为重要的是要理解C语言的内存模型(memory model),这有助于理解Objective-C的内存模型及其“引用计数”(reference counting)机制的工作原理。若要理解内存模型,则需明白:Objective-C语言中的指针是用来指示对象的。想要声明一个变量,令其指代某个对象,可用如下语法:
NSString *someString = @"The string";

上述代码声明了一个someString的变量,类型为Nsstring*,也就是说,此变量为指向Nsstring的指针,所有oc的对象都必须这样声明,对象所占内存总是分配在堆空间上,而绝不能分配在栈空间上

如果再次创建一个对象Same,那么这两个对象队徽分配在堆中,它们同时指向了堆中的NSString实例:

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

如下图所示:
在这里插入图片描述
分配在堆中的内存必须直接管理,而分配在栈上用于保存变量的内存则会在其栈桢弹出时自动清理。
Objective-C运行期环境把堆内存管理工作抽象为一套内存管理结构,名叫“引用计数”。

要点:

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

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

Objective-C采用的是头文件和实例文件区分代码,头文件.h ,实例文件.m,这里以EOCPerson为例格式如下:

// 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>
#import "EOCEmployer.h"
@interface EOCPerson : NSObject
@property (nonattomic, copy) NSString *firstName;
@property (nonattomic, copy) NSString *lastName;
@property (nonatomic, strong) EOCEmployer *employer;
@end

如果在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类的头文件,因为若要使用后者,则必须知道其所有接口细节。于是,实现文件就是:

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

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

向前上名声明的好处:

  • 解决了这两个类相互引用问题。
  • 相互引用:有两个类,它们都在头文件中引入了对方的头文件,两个类都进行各自的引用解析,这样就会导致“循环引用”(chicken-and-egg situation)。虽然我们使用#import而非#include不会导致死循环,但是这意味着两个类中有一个类无法被正确编译。
  • 但是,有时候就必须引入头文件,比如继承以及遵循的协议。

要点:

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

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

字符串字面量

不使用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。

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

编写代码时经常要定义常量。如果我们使用预处理指令,如下。

#define ANIMATION_DURATTON 0.3;

那么源代码中的ANIMATION_DURATTON字符串都会被替换为0.3,不过这样定义出的常量没有类型信息。此外,假设此指令声明在某个头文件中,那么所有引入这个头文件的代码,其ANIMATION_DURATTON都会被替换。

所以我们最好使用类型常量,如下:

static const NSTimeInterval KAnimationDuration = 0.3;

用此方法定义的常量包含类型信息,其好处是清楚地描述了常量的含义。由此可知该常量类型为NSTimeInterval,这有助于为其编写开发文档。

常量的名称与位置

常量命名法

若常量局限于某“编译单元”(也就是“实现文件”)之内,则在前面加字面k;若常量在类之外可见,则通常以类名为前缀。
位置

位置

因为Objective-C没有“名称空间”(namespace)这一概念,所以在头文件使用static const定义常量,其实等于声明了一个名叫KAnimationDuration的全局变量。此名称应该加上前缀,以表明其所属的类,例如可改为EOCViewClassAnimationDuration。

若不打算公开某个常量,则应将其定义在使用该常量的实现文件里。

// EOCAnimatedView.h
#import <UIKit/UIKit.h>@interface EOCAnimatedView : UIView
- (void)animate;
@end// EOCAnimatedView.m
#import "EOCAnimatedView.h"static const NSTimeInterval KAnimationDuration = 0.3;@implementation EOCAnimatedView
- (void)animate {[UIViewanimateWithDuration:KAnimationDuration animations:^(){// .......}];
}
@end

使用static与const来声明

static修饰符

该修饰符意味着变量仅在定义此变量的编译单元可见。假如声明此变量时不加static,则编译器会为它创建一个“外部符号”。此时若是另一个编译单元中也声明了同名变量,那么编译器就会抛出一条错误消息:

duplicate symbol _KAnimationDuration in:EOCAnimatedView.oEOCOtherView.o

const修饰符

该变量意味着变量不可修改,如果试图修改由const修饰符所声明的变量,那么编译器就会报错。

实际上,如果一个变量既声明为static,又声明为const,那么编译器就会像#define预处理指令一样,把所有遇到的变量都替换为常值。不过,用这种方式定义的常量带又类型信息。

使用extern声明全局变量

有时候需要对外公开某个常量。此时,我们需要声明一个外界可见的常值变量(constant variable)。此类常量需放在“全局符号表”(global symbol table)中,以便可以在编译单元之外使用。定义方法为:

// In the header file
extern NSString *const EOCStringConstant;// In the implementation file
NSString *const EOCtringCostant = @"VALUE";

这个常量在头文件中“声明”,且在实现文件中“定义”。在本例中,EOCtringCostant就是一个常量,这个常量是指针,指向NSString对象。

此类常量必须要定义,而且只能定义一次。通常将其定义在与声明该常量的头文件相关的实现文件里。

要点

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

5.用枚举表示状态,选项,状态码

枚举只是一种常量命名方式,某个对象所经历的各个状态、定义选项或者把逻辑含义相似的一组状态码都可以放入一个枚举集里。

编译器会为枚举分配一个独有的编号,从0开始,每个枚举递增1,也可以手动设置某个枚举成员对应的值,后面的枚举值一次加1。

可以指明枚举用的何种底层数据类型,这样编译器清楚底层数据类型的大小,可以向前声明枚举类型。

UIButton的状态:

typedef NS_ENUM(NSInteger, UIButtonRole) {UIButtonRoleNormal,UIButtonRolePrimary,UIButtonRoleCancel,UIButtonRoleDestructive
} API_AVAILABLE(ios(14.0));

要点

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

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

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

相关文章

SQL-修改表操作

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;重拾MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出现错误&am…

探索SQL性能优化之道:实用技巧与最佳实践

SQL性能优化可能是每个数据库管理员和开发者在日常工作中必不可少的一个环节。在大数据时代&#xff0c;为确保数据库系统的响应速度和稳定性&#xff0c;掌握一些实用的SQL优化技巧至关重要。 本文将带着开发人员走进SQL性能优化的世界&#xff0c;深入剖析实用技巧和最佳实践…

运用3d技术建立数字化模型---模大狮模型网

随着科技的不断进步和发展&#xff0c;3D技术已经被广泛运用于各个领域。其中&#xff0c;建立数字化模型是3D技术的一个重要应用方向。本文将从3D技术的概念、数字化模型的定义、数字化模型的建立方法、应用领域等方面介绍运用3D技术建立数字化模型的相关知识。 一、3D技术的概…

JVM内存模型深度剖析与优化

欢迎大家关注我的微信公众号&#xff1a; 目录 JVM整体结构及内存模型 JVM内存参数设置 JVM整体结构及内存模型 首先附一段简单代码&#xff0c;我们从代码层面来讲解内存模型 public class Math {public static final int initData 666;public static User user new …

【QML COOK】- 007-Item对象、信号和槽

信号&#xff08;signal&#xff09;和槽&#xff08;slot&#xff09;是Qt的独特的设计&#xff0c;自然在QML中也被支持。 Item是QML所有类型的基类&#xff0c;Item类型不会显示在窗口上&#xff0c;但是可以支持信号和槽。本节就用Item编写一个信号和槽的实例。 1. 创建Q…

【ACL 2023】 The Art of Prompting Event Detection based on Type Specific Prompts

【ACL 2023】 The Art of Prompting: Event Detection based on Type Specific Prompts 论文&#xff1a;https://aclanthology.org/2023.acl-short.111/ 代码&#xff1a;https://github.com/VT-NLP/Event_APEX Abstract 我们比较了各种形式的提示来表示事件类型&#xff0…

uniapp 打包成 apk(原生APP-云打包)免费

修改APP配置 根据需求&#xff0c;修改 manifest.json 配置&#xff0c;常见的修改有&#xff1a; 应用名称&#xff0c;应用版本名称&#xff0c;应用版本号 升级版本时&#xff0c;应用版本名称和应用版本号必须高于上一版的值 应用图标 点浏览选择png格式的图片后&#x…

智汇云舟副总裁陈虹旭受邀出席2024昆山工业元宇宙创新论坛

近日&#xff0c;由昆山市工业和信息化局、昆山经济技术开发区科技局指导&#xff0c;中国电子商会元宇宙专委会主办的2024昆山工业元宇宙创新论坛圆满举行。来自西北工业大学、中国电信股份有限公司昆山分公司、中国电信天翼云公司等单位的一百余位专家和企业领导齐聚一堂&…

在阿里巴巴,领导提拔你不是看重你的能力

很多人都在想&#xff0c;为什么领导总是不提拔你&#xff0c;难道真的是如领导给你的反馈“你的能力不行”&#xff0c;这里我想告诉大家&#xff0c;这件事情绝对没有这么简单&#xff0c;尤其是在阿里巴巴这样“江湖气”非常浓的大厂。 Part.1 领导要提拔你&#xff0c;绝对…

代码随想录刷题笔记(DAY11)

今日总结&#xff1a;继续准备期末&#xff0c;今天的算法题目比较简单&#xff0c;晚上看看能不能再整理一篇前端的笔记。 Day 11 01. 有效的括号&#xff08;No. 20&#xff09; 题目链接 代码随想录题解 1.1 题目 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff…

Linux 软连接ln -s

什么是文件软链接&#xff1f; Linux 的“软连接”与windos 的"快捷方式"相似。文件软链接&#xff08;Symbolic Link 或 SymLink&#xff09;是一种特殊的文件类型&#xff0c;它实际上是一个指向另一个文件或目录的引用。创建软链接时&#xff0c;系统会建立一个新…

【Android+物联网】Android封装MQTT连接阿里云物联网平台

前言&#xff1a; 亲测可行&#xff0c;本文实现Android封装MQTT连接阿里云物联网平台。将MQTT协议和连接阿里云平台的操作通过Android studio写入APP中&#xff0c;并简单设计UI。实现手机APP远程控制单片机LED灯亮灭的功能。 关于《Android软件开发》&#xff0c;见如下专栏…