Objective-C你了解吗--如何编写高质量有效的OC代码


这是一个朋友给的电子书上介绍的编写高质量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程序提供地图功能。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值