<<Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法>>笔记-接口与API设计
15、用前缀避免命名空间冲突
为所以名称都加上适当前缀,出现命名冲突的几率会小很多。
Apple 宣称其保留使用所有“两字母前缀”的权利,所以你自己选用的前缀应该是三个字母及以上。
不仅是类名,应用程序中的所有名称都应加前缀。如果要为既有类新增“分类”,那么一定要给“分类”及“分类”中的方法加上前缀。开发者可能会忽视另外一个容易引发命名冲突的地方,那就是类的实现文件中所有的纯 C 函数及全局变量,在编译好的目标文件中,这些名称是要算作“顶级符号”的。应该给这种 C 函数的名字加上前缀。
选择与你的公司、应用程序或者二者皆有关联的名称昨晚类名的前缀,并在所有代码中均使用这一前缀。
若自己所开发的程序中用到了第三方库,则应为其中的名称加上前缀。
16、提供“全能初始化方法”
其余初始化方法都要调用“全能初始化方法”,只有在全能初始化方法中,才会存储内部数据。当底层数据存储机制改变时,只需修改此处方法的代码就好了,无须改动其他初始化方法。
比如说,要编写一个表示矩形的类。其接口可以这样写:
#import <Foundation/Foundation.h>
@interface EOCRectangle : NSObject
@property (nonatomic, assign, readonly) float width;
@property (nonatomic, assign, readonly) float height;
@end
-(id)initWithWidth:(float)width andHeight(float)height{
if (self = [super init]) {
_width = width;
_height = height;
}
return self;
}
//设置默认值
-(id)init{
return [self initWithWidth:5.0 andHeight:10.0];
}
//抛出错误
-(id)init {
@throw [NSException
exceptionWithName:NSInternalInconsistencyException
reason:@"Must use initWithWidth:andHeight: insteade."
userInfo:nil];
}
创建名叫 EOCSquare 的类,令其成为 EOCRectangle 的子类。
#import "EOCRectangle.h"
@interface EOCSquare : EOCRectangle
-(id)initWithDimension:(float)dimension;
@end
@implementation EOCSquare
-(id)initWithDimension:(float)dimension{
return [super initWithWidth:dimension andHeight:dimension];
}
-(id)initWithWidth:(float)width andHeight:(float)height{
float dimension = MAX(width,height);
return [self initWithDimension:dimension];
}
@end
在类中提供一个全能初始化方法,并于文档里指明。其他初始化方法均应调用此方法。
若全能初始化方法与超类不同,则需要覆写超类中的对应方法。
如果超类的初始化方法不适用于子类,那么应该覆写这个超类方法,并在其中抛出异常。
17、实现 description 方法
-(NSString *)description{
return [NSString stringWithFormat:@"<%@:%p, %@>",
[self class], self,
@{@"firstName":_firstName,
@"lastName":_lastName}];
}
实现 description 方法返回一个有意义的字符串,用以描述该实例。
若想在调试时打印出更详尽的对象描述信息,则实现debugDescription 方法。
18、尽量使用不可变对象
设计类的时候,应充分运用属性来封装数据。
若某属性仅可于对象内部修改,则在“class-continuation 分类”中将其由 readonly 属性扩展为 readwrite 属性。
不要把可变的 collection 作为属性公开,而应提供相关方法,以此修改对象中的可变 collection。
19、使用清晰而协调的命名方式
起名时应该遵从标准的 Objective-C 命名规范,这样创建出来的接口更容易为开发者所理解。
方法名要言简意赅,从左至右读起来要像个日常用语中的句子才好。
方法名里不要使用缩略后的类型名称。
给方法起名时的第一要务就是确保其风格与你自己的代码或所要集成的框架相符。
20、为私有方法名加前缀
给私有方法的名称加上前缀,这样可以很容易地将其共与公共方法区分开。
不要单用一个下划线做私有方法的前缀,因为这种做法是预留给苹果公司用的。
21、理解 Objective-C 错误模型
只有发生了可使用整个应用程序崩溃的严重错误时,才应使用异常。
在错误不那么严重的情况下,可以指派“委托”来处理错误,也可以把错误信息放在 NSError 对象里,经由“输出参数”放回给调用者。
22、理解 NSCopying 协议
想令自己的类支持拷贝操作,那就要遵守并实现 NSCopying 协议,该协议只有一个方法:
-(id)copyWithZone:(NSZone *)zone;
实现协议中规定的方法:
-(id)copyWithZone:(NSZone *)zone{
Person * copy = [[[self class] allocWithZone:zone] init];
return copy;
}
NSMutableCopying协议,该协议也只有一个方法:
-(id)mutableCopyWithZone:(NSZone *)zone;
无论当前实例是否可变,若需要获取其可变版本的拷贝,均应调用mutableCopy 方法。若需要拷贝不可变的拷贝,则应通过 copy 方法来获取。
对于不可变的 NSArray 与可变的 NSMutableArray 来说,下列关系总是成立的:
[NSMutableArray copy] => NSArray
[NSArray mutableCopy] => NSMutableArray
在编写拷贝方法时,还要决定一个问题,就是应该执行“深拷贝”还是“浅拷贝”。深拷贝的意思就是:在拷贝对象自身时,将其底层数据也一并复制过去。Foundation 框架中的所有 collection 类在默认情况下都执行浅拷贝,也就是说,只拷贝容器对象本身,而不复制其中数据。
以 NSSet 为例,该类提供了厦门这个初始化方法,用以执行深拷贝:
-(id)initWithSet:(NSArray *)array copyItems:(BOOL)copyItems
若 copyItems 参数设为 YES,则该方法会向数组中的每个元素发送 copy 消息,用拷贝好的元素创建新的 set,并将其返回给调用这。
若想令自己所写的对象具有拷贝功能,则需要实现 NSCopying 协议。
如果自定义的对象分为可变版本与不可变版本,那么就要同时实现 NSCopying 与 NSMutableCopying 协议。
赋值对象时需要决定采用钱拷贝还是深拷贝,一般情况下应该尽量执行浅拷贝。
如果你所写的对象需要深拷贝,那么可考虑新增一个专门执行深拷贝的方法。