Effective Objective-C 2.0 学习笔记(1)

第一条 了解Objective-C预言起源

起源:Smalltalk

类型:使用消息结构的语言

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

对象分配在堆空间上,指针分配在栈空间上

结构体分配在栈空间(CGRect)

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

用OC编写任何类几乎都需要引入Foundation.h。

向前声明:@class EOCEmployer;

使用#import而非#include互相引用时不会死循环

要点:

1、除非有必要,不要引入头文件。应在类的头文件中使用向前声明来提及别的类,在实现文件中引入那个类的头文件。可以尽量降低类之间的耦合。

2、无法使用向前声明,如声明某个类遵循一项协议。尽量把“遵循某协议”的声明移到“class-continuation分类”中。若不行,就把协议单独放在一个头文件中,再引入。

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

字面量语法只是一种“语法糖”

疑问:mrc下字面量创建的对象到底有没有reatain+1

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

要点:

1、使用字面量语法来创建字符串、数值、数组、字典。

2、通过取下标操作来访问数组下标或字典中的键所对应的元素

3、用字面量语法创建数组或字典时,若值中有nil,则会抛出异常。因此,务必确保值里不含nil

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

#define ANIMATION_DURATION 0.3
static const NSTimeInterval kAnimationDuration = 0.3;

若不打算公开某个常量,则应将其定义在使用该常量的实现文件里。static修饰符意味着该变量仅在定义此变量的编译单元可见。在OC语境下,“编译单元”指每个类的实现文件(以.m为后缀名)。假如声明此变量时不加static,则编译器会为它创建一个“外部符号”。此时若是另一个编译单元中也声明了同名变量,那么编译器就会抛出错误。

如果一个变量既声明为static,又声明为const,编译器根本不会创建符号,而是会像#define一样,把所遇到的变量都替换成常值。

常量放在“全局符号表中”:

//In the header file
extern NSString *const EOCStringConstant;
//In the implementation file
NSString *const EOCStringConstant = @"VALUE";
Effective Objective-C 2.0 学习笔记(1)

第五条 用枚举表示状态、选项、状态吗

要点:

如果把传递给某个方法的选项表示为枚举类型,而多个选项又可同时使用,那么就将各选项值定义为2的幂,以便通过按位或操作将其组合起来。

用NS_ENUM与NS_OPTIONS宏定义枚举类型,并指明底层数据类型。这样可以确保枚举是用开发者所选的底层数据类型实现出来,而不会采用编译器所选的类型。

在处理枚举类型的switch语句中不要实现default分支。这样的话,加入新枚举之后,编译器就会提示开发者:switch语句并未处理所有枚举。

第六条 理解 “属性” 这一概念

编译器会把“点语法”转换为对存取方法的调用,使用“点语法”的效果与直接调用存取方法相同。

如果使用了属性,编译器会自动编写访问这些属性所需的方法,此过程叫“自动合成(autosynthesis)”。这个过程由编译器在编译期执行,所以编辑器里看不到“合成方法(synthesied method)”的源代码。除了生成方法代码,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字。也可以在类的实现代码里通过@synthesize语法来指定实例变量的名字

@implementation EOCPerson
@synthesize firstName = _myFirstName;
@synthesize lastName = _myLastName;
@end

有一种办法能阻止编译器自动合成存取方法,就是使用@dynamic关键字,它会告诉编译器:不要自动创建实现属性所用的实例变量,也不要为其创建存取方法。而且,在编译访问属性的代码时,即使编译器发现没有定义存取方法,也不会报错,它相信这些方法能在运行期找到。

@interface EOCPerson : NSManagedObject
@property NSString *firstName;
@property NSString *lastName;
@end
@implementation EOCPerson
@dynamic firstName, lastName;
@end

属性的特质:原子性,读/写权限,内存管理语义,方法名

要点:

用@property语法来定义对象中所封装的数据

通过“特质”来指定存储数据所需的正确语义

在设置属性所对应的实例变量时,一定要遵从该属性所声明的语义

开发iOS程序时应该使用nonatomic属性,因为atomic属性会严重影响性能

第七条 在对象内部尽量直接访问实例变量

直接访问实例变量,不会触发 kvo通知。

在写入实例变量时,通过设置方法来做,在读取实例变量时,则直接访问。

在初始化方法中设置属性值应该直接访问实例变量,因为子类可能会覆写设置方法

delloc中直接通过

惰性化初始技术需要采用存取方法。

第八条 理解“对象等同性” 这一概念

使用NSObject协议中声明的 isEqual 方法来判断两个对象的等同性

NSObject协议中两个用于判断等同性的关键方法:

- (BOOL)isEqual:(id)object;
- (NSUInterger)hash;

等同性约定

如果 isEqual: 判定两个对象相等,那么hash方法也必须返回同一个值.但是,如果两个对象的hash方法返回同一个值, isEqual: 未必会认为两者相等

有一种情况要注意,在容器中放入可变类对象时,把某个对象放入collection之后,就不应再改变其哈希码了。

要点:

若想检测对象的等同性,请提供“isEquel:”与hash方法

相同的对象必须具有相同的哈希码,但是两个哈希码相同的对象却未必相同

第九条 以“类族模式”隐藏实现细节

类族可以隐藏“抽象基类”背后的实现细节。

系统框架有很多类族。大部分collection类都是类族。

在传统的类族模式中,通常只有一个类具备“公共借口”,这个类就是类族中的抽象基类

Cocoa中NSArray这样的类族来说,新增子类需遵守几条规则:

自类应该继承自类族中的抽象基类

子类应该定义自己的数据存储方式

子类应当覆写超类文档中指明需要覆写的方法

第十条 在既有类中使用关联对象存放自定义数据

Effective Objective-C 2.0 学习笔记(1)

设置关联对象

void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)

取关联对象值

id objc_getAssociatedObject(id object, void *key)

移除指定对象的全部关联对象

void objc_removeAssociatedObjects(id object)

要点

定义关联对象可指定内存管理语义,用以模仿定义属性时所采用的“拥有关系”与“非拥有关系”

只有在其他做法不可行时才应选用关联对象,因为这种做法通常会引入难于查找的bug



分享到:


相關文章: