做药的常用网站有哪些,广州工商局官网,专做正品 网站,如何建立自己免费网站【Effective Objective - C】—— 熟悉Objective-C 熟悉Objective-C1.oc的起源消息和函数的区别运行期组件和内存管理要点#xff1a; 2.在类的头文件中尽量少引入其他头文件向前声明要点#xff1a; 3.多使用字面量语法#xff0c;少用与之等价的方法字符串字面量字面数值字… 【Effective Objective - C】—— 熟悉Objective-C 熟悉Objective-C1.oc的起源消息和函数的区别运行期组件和内存管理要点 2.在类的头文件中尽量少引入其他头文件向前声明要点 3.多使用字面量语法少用与之等价的方法字符串字面量字面数值字面数组字面字典局限性要点 4.多用类型常量少用#define预处理指令常量的名称与位置常量命名法位置 使用static与const来声明static修饰符const修饰符 使用extern声明全局变量要点 5.用枚举表示状态选项状态码要点 熟悉Objective-C
Objective-C通过一套全新语法在C语言基础上添加了面向对象特性。Objective-C的语法中频繁使用方括号而且不吝于写出极长的方法名这通常令许多人觉得此语言较为冗长。其实这样写出来的代码十分易读只是C或Java程序员不太能适应。 Objective-C语言学起来很快但有很多微妙细节需注意而且还有许多容易为人所忽视的特性。另一方面有些开发者并未完全理解或是容易滥用某些特性导致写出来的代码难于维护且不易调试。本章讲解基础知识后续各章谈论语言及其相关框架中的各个特定话题。
1.oc的起源
和CJava一样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.hinterface EOCPerson : NSObject
property (nonattomic, copy) NSString *firstName;
property (nonattomic, copy) NSString *lastName;
end//EOCPerson.m
#import EOCPerson.himplementation 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.hclass EOCEmployer;interface EOCPerson : NSObject
property (nonattomic, copy) NSString *firstName;
property (nonattomic, copy) NSString *lastName;
property (nonatomic, strong) EOCEmployer *employer;
endEOCPerson类的实现文件则需引入EOCEmployer类的头文件因为若要使用后者则必须知道其所有接口细节。于是实现文件就是
//EOCPerson.m
#import EOCPerson.h
#import EOCEmployer.himplementation 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.hinterface EOCAnimatedView : UIView
- (void)animate;
end// EOCAnimatedView.m
#import EOCAnimatedView.hstatic const NSTimeInterval KAnimationDuration 0.3;implementation EOCAnimatedView
- (void)animate {[UIViewanimateWithDuration:KAnimationDuration animations:^(){// .......}];
}
end使用static与const来声明
static修饰符
该修饰符意味着变量仅在定义此变量的编译单元可见。假如声明此变量时不加static则编译器会为它创建一个“外部符号”。此时若是另一个编译单元中也声明了同名变量那么编译器就会抛出一条错误消息
duplicate symbol _KAnimationDuration in:EOCAnimatedView.oEOCOtherView.oconst修饰符
该变量意味着变量不可修改如果试图修改由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的幂以便通过按位或操作将其组合起来。用NSENUM与NSOPTIONS宏来定义枚举类型并指明其底层数据类型。这样做可以确保枚举是用开发者所选的底层数据类型实现出来的而不会采用编译器所选的类型。在处理枚举类型的switch语句中不要实现default分支。这样的话加入新枚举之后编译器就会提示开发者switch 语句并未处理所有枚举。