Effective Objective-C 的读书笔记

2017-09-09  本文已影响9人  stringwu

主要摘录了《 Effective Objective-C》里的编写高质量的方法;

1 熟悉Objective -C

1.1 OC 起源

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

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

有时候在编写头文件时,需要引入某个类A(如作为当前类的某个属性来使用),但是不需要知道这个类A的实现细节,此时我们不需要直接引入这个类A的头文件,只需要告诉编译器,类A 是一个类就可以了,然后在实现文件里再引入类A的头文件; 向前声明的语法为: @class 类A ;

1.3 多用字面量语法 ,少用与之等价的方法

如 :

NSNumber *someNumber = [NSNumber numberWithInt:1];
// 字面量语法
NSNumber *someNumber = @1;

NSDictionary *personData = [NSDictionarydictionaryWithObjectsAndKeys:
@"Matt",@"firstName",@"Ga",@"lastName",nil];
// 字面量语法
NSDictionary *persionData= @{@"firstName" : @"Matt",@"lastName" : @"Ga"};

NSString *lastName = [personData objectForKey:@"lastName"];
// 字面量语法
NSString *lastName = personData[@"lastName"];

由以上几个例子中,可以很明显可以看到字面量语法全更加简洁直观;但字面量语法有个小限制,就是除了字符串以外,所创建出来的对象必须属于 Foundation框架;

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

全名法则: 若常量仅在编译单元内可见,则在前面加字母k,如果在类外可见,则通常以类名为前缀;

1.5 用枚举表示状态、选项、状态码

2 对象、消息、运行期

2.1 理解“属性”这一概念

2.2 在对象内部尽量直接访问实例变量

2.3 理解”对象等同性“这一概念

==操作符比较的是两个指针本身,而不是其所指对象,一般常用 isEqual方法来判断两个对象的等同性;

2.4 以“类族模式” 隐藏实现细节

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

2.6 理解 objc_msgSend 作用

在OC中,如果向对象传递消息,那就会使用动态绑定机制来决定需要调用的方法。而在底层,所有方法都是普通的C语言函数,然而对象在接收到消息后,究竟该调用哪个方法则完全于运行期决定。如:

//以下语句是给对象发送一条消息
int value = [someObject messageName :parameter];

其中,someObject叫接收者,messageName 叫选择子(selector),选择子和参数合起来称为消息;

2.7 理解消息转发机制

2.8 用“方法调配技术”调试“黑盒方法”

2.9 理解”类对象“的用意

isMemberOfClass 能够判断出对象是否为某个特定类的实例;
isKindOfClass 能够判断出对象是否为某类或其派生类的实例;

3 接口与API设计

3.1 用前缀避免命名空间冲突

OC 没有其他语言那种内置的命名空间机制。因此我们在起名时需要设法避免潜在的例句冲突。我们在选择前缀时,应该是三个字母的;

3.2 提供“全能初始化方法”

全能初始化方法类似于 Java 中提供不同构造参数的构造方法,所有的构造方法最终都会调用其中参数最完整的构造方法;

3.3 实现 description 方法

description 方法类似于 JavaObjecttoString 方法的功能,而且在调试时,如果有实现debugDescription 方法,则会调用该方法来输出更详细的信息;

3.4 尽量使用不可变对象

3.5 使用清晰而协调的命名方式

3.6 为私有方法名加前缀

3.7 理解 OC 错误类型

如果出现非致命的错误时,则可以令方法返回 nil/0 或使用 NSError 来表明其中有错误发生;

3.8 理解 NSCopying 协议

一般情况下,遵从了NSCopying协议的对象,执行的都是浅拷贝,除非该对象有特别说明它是用深拷贝来实现Copying,否则应该自己去编写深拷贝的;

4 协议与分类

4.1 通过委托与数据源协议进行对象通信

OC 中广泛使用 delegate pattern 的模式来实现对象间的通信,该模式的主旨是:定义一套接口,某对象若想接受另一对象的委托,则需遵从此接口; 其实这就是 Java里的编程规则里的面向接口编程;所谓的位段结构体,就是用一个属性来表明委托对象实现了哪些协议方法,每个协议方法对应于该属性的一个二进制位;
需要注意的是 委托对象与被委托的对象之间的关系应该是非拥有关系,也就是对应的属性得用weak来修饰;

4.2 将类的实现代码分散到便于管理的数个分类之中

4.3 总是为第三方类的分类名称加前缀

分类机制通常用于向无源码的既有类中新增功能,分类中的方法是直接添加在类里面的;

如:

//给NSString 添加一个分类:ABC_HTTP
@interface NSString(ABC_HTTP)
- (NSString *) abc_urlEncodeString;

@end

4.4 勿在分类中声明属性

4.5 使用class-continuation分类 隐藏实现细节

例子:

// EOCPerson.h
@interface EOCPerson :NSObject {
    .....
}

//EOCPerson.m
//这是EOCPerson的`class-continuation`分类
@interface EOCPerson(){
    .....
}

@implementation EOCPerson {
    ...
}

### 4.6 通过协议提供匿名对象
- 协议可在某种程度上提供匿名类型,具体的对象类型可以淡化成遵从某协议的 id 类型,协议里规定了对象所应实现的方法;
- 使用匿名对象来隐藏类型名称;
- 如果具体类型不重要,重要的是对象能够响应特定方法,那么可使用匿名对象来表示;

OC 里的协议 就是 Java 里的接口,协议定义了一系列方法,遵从些协议的对象实现它们


5 内存管理

5.1 理解引用计数

5.2 以 ARC 简化引用计数

若方法名以下列词语开头,则其返回的对象归调用者所有:

5.3 在 dealloc 方法中只释放引用并解除监听

5.4 编写 “异常安全代码”时留意内存管理问题

5.5 以弱引用避免保留环

避免保留环的最佳方式就弱引用,这种引用经常用来表示“非拥有关系”(nonowning relationship)。将属性声明为unsafe_unretained即可。

5.6 以“自动释放池块” 降低内存峰值

释放对象有两种方式:一种是调用realease方法,使其保留计数立即递减;另一种是调用autorealease方法,将其加入“自动释放池”中。自动释放池用于存放那些需要在稍后某个时刻释放的对象;

5.7 用“僵尸对象”调试内存管理问题

5.8 不要使用retainCount

6 块与大中枢派发

6.1 理解“块”这一概念

块所占的内存区域是分配在栈中的。这也就是说块只在定义它的那个范围内有效。

6.2 为常用的块类型创建 typedef

6.3 用 handler 块降低代码分散程度

6.4 用块引用其所属对象时不要出现保留环

6.5 多用派发队列,少用同步锁

6.6 多用 GCD,少用 @performSelector系统方法

6.7 掌握 GCD 及操作队列的使用时机

在执行后台任务时,GCD 并不一定是最佳方式,还有一种技术叫 NSOperationQueue,它虽然与 GCD 不同,但是却与之相关,开发者可以把操作以 NSOperation子类的形式放在队列中,而这些操作也能够并发执行;
使用NSOperationNSOperationQueue的好外:

6.8 通过 Dispatch Group机制,根据系统资源状况来执行任务

dispatch group 是 GCD 的一项特性,能够把任务分组。调用者可以等待这组任务执行完毕,也可以在提供回调函数之后继续往下执行,这组任务完成时,调用者会得到通知;

6.9 使用diapatch_once 来执行只需要运行一次的线程安全代码

常用 diapatch_once来编写线程安全的单例;

6.10 不要使用dispatch_get_current_queue

7 系统框架

7.1 熟悉系统框架

7.2 多用块枚举,少用 for 循环

NSEnumerator遍历法

NSEnumerator是个抽象基类,其中只定义了两个方法,供子类来实现:

想遍历数组时,则可以这样来写代码:

NSArray *anArray = .....;
NSEnumerator *enumerator = [anArray objectEnumeator];
id object;
while((object = [enumerator nextObject])!= nil) {
    //dosomething with object
}

遍历字典时,也可以使用类似的代码。并且NSEnumerator 有多种枚举器供选择,如反向遍历等,使用时可以根据需要选择不同的枚举器;

快速遍历

快速遍历其实就是在基本for循环的基础上加了个 in关键字:

for(id object in anArray){}

基于块的遍历方式

NSArray 中定义了下面的方法,实现最基本的遍历功能:

-(void)enumeratorObjetsUsingBlock:(void(^)(id object,NSUInteger idx,BOOL *stop))block;

还有其他类似的遍历方法,可以接受各种选项来控制遍历操作

7.3 对自定义其内存管理语义的 collection 使用无缝桥接

无缝桥接就是用来对Foundation框架和CoreFoundation 框架中的等价的类进行转换,
简单的无缝桥接:

NSArray *anArray = ......;
CFArrayRef afarray = (__bridge CFArrayRef)anArray;

关键字__bridge告诉 ARC 如何处理转换所涉及的 OC 对象,而反向转换(CoreFoundation 类 转换为等价的 Foundation 类 )则使用关键字__bridge_transfer 来实现;

7.4 构建缓存时选用 NSCache 而非 NSDictionary

7.5 精简 initialize 与 load 的实现代码

7.6 别忘了 NSTimer 会保留其目标对象

上一篇 下一篇

猜你喜欢

热点阅读