前言
这本书大致看过一次,但没有做笔记。
不做笔记,总觉得没学过。。。
那么,再看一遍,做下笔记
1. 了解Objective-C语言的起源
OC为C语言添加了面向对象特性,是其超集。OC使用动态绑定的消息结构,也就是说,在运行时才会检查对象类型。
2. 在类的头文件中尽量 少 引入其他头文件
在.h文件中,需要引入其他类A时,如果并不需要知道类A的具体细节,只需要知道有一个类名叫A即可,那么,可以使用@class A;
,然后在.m文件中,真正用到类A时,再在头部引入:#import "A.h";
将引入头文件的时机尽量延后,只在确有需要时才引入,这样可以减少类的使用者所需要引入的头文件数量。
引入A.h的时候,会将A里面所有的内容都引入进去,这样会引入许多用不到的内容,增加编译时间
问题是,在.h中使用@class,在.m中引入A,那么A的内容同样全部引入到了.m文件中
还有个在实际开发中的问题,增加了代码量,虽然是多了一行,愿意写不写,也是个问题。
3. 多用字面量语法,少用与之等价的方法
字符串字面量:NSString *someString = @"this is a string";
字面数值:NSNumber *someNumber = @1;
此外,还有:字面量数组、字面量字典
使用字面量语法创建出来的 字符串、数组、字典 对象都是不可变的。
若想使用可变版本的对象,则需要复制一份:NSMutableArray *mutable = [@[@1, @2, @3, @4, @5] mutableCopy];
应该通过取下标操作来访问数组下标 或 字典中的键所对应的元素
4. 多用类型常量,少用#define预处理指令
多使用:static const NSTimerInterval kAnimationDuration = 0.3;
,少用:#define ANIMATION_DURATION 0.3
- 使用static const常量,带有类型信息
常量命名建议方法:
- 若常量局限在某个.m文件之内,则在前面加上字母k
- 若常量类之外可见,则通常以类名为前缀。
编译器每接收到一个“编译单元”(以.m为后缀名),就会输出一份“目标文件”(object file)
在定义通知的“全局符号表”(global symbol table)中,使用:
.h中:extern NSString *const XXXNAME;
.m中:NSString *const XXXNAME = @"VALUE";
在头文件(.h)文件中“声明”
在实现文件(.m)文件中“定义”
5. 用枚举表示状态、选项、状态码
某个对象所经历的各种状态就可以定义为一个简单的枚举集(enumeration set)
在定义选项的时候,特别是这些选项可以彼此组合的时候,此时,更适合用enum
组合这种,只要使用了位运算操作:<< |
对象、消息、运行期
6. 理解“属性”这一概念
“属性”(property)是OC的一项特性,用于封装对象中的数据。
访问属性,可以使用“点语法”,编译器会把“点语法”转换为对存取方法的调用
使用@property属性,编译器做的事情:
- 自动生成setter方法、getter方法
- 自动添加适当类型的实例变量,并在属性名前面加下划线,作为实例变量的名字
@dynamic关键字会告诉编译器:
- 不要自动创建实现属性所用的实例变量
- 不要为其创建存取方法
属性特质
@property (nonatomic, readwrite, copy) NSString *firstName;
属性拥有四类特质:
原子性、读写权限、内存管理语义、方法名
原子性
默认情况下,由编译器所合成的方法会通过锁定机制确保其原子性(atomic)
但,开销比较大,因此,异步不使用atomic
nonatomic,不使用同步锁
读写权限
readwrite,默认方法,拥有getter和setter方法
readonly,只有getter方法
内存管理语义
属性用于封装数据,而数据则要有“具体的所有权语义”
- assign: 针对“纯量类型”(CGFloat 或 NSInteger等)的简单赋值
- strong: 此特质表明该属性定义了一种“拥有关系”。为这种属性设置新值时,设置方法会先保留新值,并释放旧值,然后再将新值设置上去
- (void)setFoo:(id)foo {
[foo retain];//保留新值
[_foo release];//释放旧值
_foo = foo;//更新实例变量
}
- weak: 此特质表明该属性定义了一种“非拥有关系”。在属性所指的对象遭到摧毁时,属性值也会清空(nil out)
- unsafe_unretained: 与assign相同,但也适用于“对象类型”,表示“非拥有关系”,当目标对象摧毁时,不会自动清空
- copy: 与strong类似。然而设置方法并不保留新值,而是将其copy
方法名
@property (nonatomic, getter = isOn) BOOL on;
7. 在对象内部尽量直接访问实例变量
在对象之外访问实例变量时,总是应该通过属性来访问。
在对象之内,有“通过属性访问”与“直接访问”两种方法,本书建议:
- 在读取实例变量的时候,采用“直接访问“
- 在设置实例变量的时候,采用”通过属性访问“
- 直接访问实例变量不经过”方法派发“(method dispatch),编译器所生成的代码会直接访问保存对象实例变量的那块内存,所以速度快
- 直接访问实例变量,不会触发KVO
- 在初始化方法中,总是 直接访问实例变量
- 在懒加载中,必须通过setter方法访问属性,不能通过下划线直接访问
- 在dealloc方法中,直接通过实例变量来读写数据
8. 理解”对象等同性“这一概念
”等同性“(equality),来比较对象
”==“操作符比较的是两个指针本身,指针相等才返回YES
NSObject协议中,有一个”isEqual“方法:该方法首先会判断是否为同一个类,然后再来判断两个对象的等同性。此等同性,指的是:指针值完全相等
NSObject里面有两个重要的方法:
若两对象相等,则其哈希码也相等。
但两个哈希码相同的对象却未必相等。
而NSString就厉害了,有个"isEqualToString"方法,直接判断两个字符串内容是否相同
NSString中,isEqualToString比isEqual快,因为后面还需要执行额外步骤,因为它不知道受测对象的类型
同样,NSArray与NSDictionary类也有特殊的等同性判定方法:isEqualToArray:
、isEqualToDictionary:
使用这两个方法,需要保证所比较类型为对应正确类型
9. 以“类簇模式”隐藏实现细节
其实,有点多态的意思
NSArray、NSDictionary都是类簇,是基类。也叫做:抽象基类
- 子类应该继承自类簇中的抽象基类
- 子类应该定义自己的数据存储方式
NSArray本身只是包在其他隐藏对象外面的壳,它仅仅定义了所有数组都需具备的一些接口。 - 子类应当覆写超类文档中指明需要覆写的方法
10. 在既有类中使用关联对象存放自定义数据
关联对象(Associated Object)
- 可以给某对象关联许多其他对象,这些对象通过“键”来区分。
- 可以通过“关联对象”机制,来把两个对象连起来
- 在设置关联对象值时,通常使用静态全局变量做键
关联对象的几个重要方法:void objc_setAssociatedObject()
id objc_getAssociatedObject()
void objc_removeAssociatedObjects()