第一条 了解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";
第五条 用枚举表示状态、选项、状态吗
要点:
如果把传递给某个方法的选项表示为枚举类型,而多个选项又可同时使用,那么就将各选项值定义为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这样的类族来说,新增子类需遵守几条规则:
自类应该继承自类族中的抽象基类
子类应该定义自己的数据存储方式
子类应当覆写超类文档中指明需要覆写的方法
第十条 在既有类中使用关联对象存放自定义数据
设置关联对象
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
閱讀更多 大大眾小小粽 的文章