这是一个朋友给的电子书上介绍的编写高质量iOS 与 OS X代码的有效方法,在此总结了一下分享给大家。
用Objective-C语言编写任何类几乎都需要引入Foundation.h。如果不在该类本身引入这个文件的话,那么就要引入与其超类所属框架相对应的“基本头文件”(base header file)如UIKit.h;
下面来说一些oc的小细节
1、当编译一个A类文件的时候,不需要知道A类的全部细节,只需要知道一个类名A就好,可以用@class A告诉编译器。
2、在类的头文件中尽量少引入其他的头文件。
当需要导入多个#import的时候,最好考虑一下是否有必要,如果可以用声明取代引入头文件,则应尽量将其移至“class-continuation分类”中,这样做不进可以缩减编译时间,而且还能降低彼此依赖程度。
3、多用字面量语法,少用与之等价的方法。
如NSNumber * someNumber = [NSNumber numberWithInt:1];
字面量语法就是:NSNumber * intNumber = @1;
如NSArray * animals = [NSArray arrayWithObjects:@"cat",@"dog",@"mouse",@"badger",nil];
使用字面量语法来创建就是:NSArray * animals = @[@"cat",@"dog",@"mouse",@"badger"];
4、多用类型常量,少用#define预处理指令。
预处理定义出来的常量不含有类型信息,编译器只是会在编译前据此执行查找与替换操作。即使有人重新定义了常量值,编译器也不会产生警告信息,这将导致应用程序中的常量值不一致。
在实现文件中使用stain const 来定义“只在编译单元内可见的常量”。由于此类常量不在全局符号表中,所以无须为其名称加前缀。
在头文件中使用extern来声明全局常量,并在相关实现文件中定义其值。这种常量要出现在全局符号表中,所以其名称应加以区隔,通常用与之相关的类名做前缀。
5、用枚举表示状态、选项、状态码
枚举是一种常量命名方式。某个对象经历的各个状态就可以定义为一个简单的枚举集,比如说:可以用下列枚举表示“套接字连接”(scoket connection)的状态:
enum EoCConectionState
{
EOCConnectionStateDisConnected,
EOCConectionStateConnecting,
EOCConnectionStateConnected;
};
由于每一种状态都用于一个便于理解的值来表示,所以这样写出来的代码更易读懂。编译器会为枚举分配一个独有的编号,从0开始,每个枚举递增1.实现枚举所用的数据类型取决去编译器,不过其二进制(bit)的个数必须能完全表示下枚举编号才行。在前例中,由于最大编号是2,所以使用1个字节的char类型即可。
如果把传递给某个方法的选项表示为枚举类型,而多个选项又可同时使用,那么就将各选项值定义为2的幂,以便通过按位或操作将其组合起来。
用NS_ENUM 与 NS_OPTIONS宏来定义枚举类型,并指明其底层数据类型。这样做可以确保枚举是用于开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型。
在处理枚举类型的switch语句中不要实现default分支,这样的话,加入新枚举之后,编译器就会提示开发者:switch语句并未处理所有枚举。
6、理解“属性"这一概念
"属性"是Objective-C的一项特性,用于封装对象中的数据。Objective-C对象通常会把所需要的数据保存为各种实例变量。属性具有四项特质:原子性,读写权限,内存管理语义,方法名。
7、在对象内部尽量直接访问实例变量
在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,则应通过属性来写。
在初始化方法及dealloc方法中,总是应该直接通过实例变量来读写数据。
有时会使用惰性初始化技术配置某份数据,这种情况下,需要通过属性来读取数据。
8、理解"对象等同性"这一概念
根据对象等同性约定:若两对象相等,则其哈希码也相等,但是两个哈希码相等的对象却未必相等。这是能否正确覆盖“isEqual:”方法的关键所在。下面举例一种哈希码的计算方法:
- (NSUInteger)hash
{
NSString * stringToHash = [NSStringstringWithFirmat:@"%@:%@:%i",_firstName,_lastName,_age];
return [stringToHash hash];
}
总结:
若想检测对象的等同性,请提供“isEqual:”与hash方法。
相同的对象必须具有相同的哈希码,但是两个哈希码相同的对象却未必相同。
不要盲目地逐个检测每条属性,而是应该依照具体需求来制定检测方案。
编写hash方法时,应该使用计算速度快而且哈希码碰撞几率低的算法。
9、以“类簇模式”隐藏实现细节
“类簇”是一种很有用的模式,可以隐藏“抽象基类”背后的实现细节。把实现细节隐藏在一套简单的公共接口后面。
系统的框架中经常使用类簇。
从类簇的公共抽象基类中继承子类时要当心,若有开发文档,则应首先阅读。
10、在既有类中使用关联对象存放自定义数据。
有时需要在对象中存放相关信息,这时我们通常会从对象所属的类中继承一个子类。然后改用这个子类对象。然而并非所有情况下都能这么做,有时候类的实例可能是由某种机制锁创建的,而而开发者无法令这种机制创建出自己所写的子类实例。这就引出了关联对象。
总结:
可以通过“关联对象”机制来把两个对象连起来。
定义关联对象时可指定内存管理语义,用以模仿定义属性时所采用的“拥有关系”与“非拥有关系”。
只有在其他做法不可行时才应选用关联对象,因为这种做法通常会引入难于查找的bug。
11、理解objc_msgSend的作用
即“传递消息”。
消息由接收者、选择子及参数构成。给某对象“发送消息”也就相当于在该对象“调用方法”。
发给某对象的全部消息都由“动态消息派发系统”来处理。该系统会查出对应的方法,并执行其代码。
12、理解消息转发机制
消息转发分为两大阶段。第一阶段先征询接受者,所属的类,看是否能动态添加方法,以处理当前这个“未知的选择子(unknown selector)”,这叫做“动态方法解析”。第二阶段涉及“完整的消息转发机制”。
若对象无法响应某个选择子,则进入消息转发流程。
通过运行期的动态解析功能,我们可以在需要用到某个方法时再将其加入类中。
对象可以把其无法解读的某些选择子转交给其他对象来处理。
经过上面的步骤之后,如果还是没办法处理选择子,那就启动完整的消息转发机制。
13、用“方法调配技术”调试“黑盒方法”
在运行期,可以向类中新增或者替换选择子所对应的方法实现。
使用另一份实现来替换原有的方法实现,这道工序叫做“方法调配”,开发者常用此技术向原有实现添加新功能。
一般来说,只有调试程序的时候才需要在运行期修改方法实现,这种做法不宜滥用。
14、理解“类对象”的用意
每个实例都有一个指向Class对象的指针,用以表明类型,而这些Class对象则构成类的继承体系。
如果对象类型无法在编译期确定,那么就应该使用类型信息查询方法来探知。
尽量使用类型信息查询方法来确定对象类型,而不要直接比较类对象,因为某些对象可能实现了消息转发功能。
15、用前缀避免命名空间冲突
选择与你公司、应用程序或者二者皆有关联之名称作为类名的前缀,并在所有代码中均使用这一前缀。
若自己所开发的程序库中用到了第三方库,则应为其中的名称加上前缀。
16、提供“全能初始化方法”
在类中提供一个全能初始化方法,并于文档里指明,其他初始化方法均应调用此方法。
若全能初始化方法与超类不同,则需覆写超类(SuperClass)中的对应方法。
如果超类的初始化方法不适用于子类,那么应该覆写这个超类方法,并在其中抛出异常。
17、实现description
实现description方法返回一个有意义的字符串,用以描述该实例。
若想在调试时打印出更详尽的对象描述信息,则应实现debugDescription方法。
18、尽量使用不可变对象
尽量创建不可变对象
若某属性仅可于对象内部修改,则在“class-continuation分类”中将其由readonly属性扩展为readwrite属性。
不要把可变的collection作为属性公开,而应提供相关方法,以此修改对象中可变collection。
19、理解NSCopying协议
若想令自己所写的对象具有拷贝功能,则需实现NSCopying协议。
如果自定义的对象分为可变版本与不可变版本,那么就要同时实现NSCopying与NSMutableCopying协议。
复制对象时需决定采用浅拷贝还是深拷贝,一般情况下应该尽量执行浅拷贝。
如果你所写的对象需要深拷贝,那么可考虑新增一个专门执行深浅拷贝的方法。
20、多用GCD,少用performSelector系列方法
performSelector系列方法在内存管理方面容易有疏忽,它无法确定将要执行的选择子具体是什么,因而ARC编译器也就无法插入适当的内存管理方法。
performSelector系列方法所能处理的选择子太过局限了,选择子的返回值类型及发送给方法的参数个数都受到限制。
如果想把任务放在另一个线程上执行,那么最好不要用performSelector系列方法,而是应该把任务封装到块里,然后调用大中枢派发机制的相关方法来实现。
使用GCD中的dispatch_once来执行只需运行一次的线程安全代码
dispatch_get_current_queue函数的行为常常与开发者所预期的不同,从函数已经废弃,只应做调试之用。
21、熟悉系统框架
Foundation 就不多说了,它可以说是OC应用程序的“基础”。
CFNetwork 此框架提供了C语言级别的网络通信能力,它将“BSD套接字”抽象成易于使用的网络接口,而Foundation则将该框架里的部分内容封装为Objective-C语言的接口,以便进行网络通信,例如可以用NSURLConnection从URL中下载数据。
CoreAudio 该框架所提供的C语言API可用来操作设备上的音频硬件。
AVFoundation 此框架所提供的Objective-C对象可用来回放并录制音频及视频,比如能够在UI视图类里播放视频。
CoreData 此框架所提供的Objective-C接口可将对象放入数据库,便于持久保存。
CoreText 此框架提供的C语言接口可以高效执行文字排版及渲染操作。
CoreAnimation 是用于Objective-C语言写成的,他提供了一些工具,而UI框架则用这些工具来渲染图形并播放动画。开发者编程时可能从来不会深入到这种级别,不过知道该框架总是好的。CoreAnimation本身并不是框架,它是QuartzCore框架的一部分。然而在框架的国度里,CoreAnimation扔应算作“一等公民”。
CoreGraphics框架以C语言写成,其中提供了2D渲染所必备的数据结构与函数。
MapKit框架,它可以为iOS程序提供地图功能。