第15条:用前缀避免命名空间冲突
Objective-C没有其他语言内置的命名空间(namespace)机制。如果发生命名冲突程序连接时候,出现以下错误:
duplicate symbol _OBJC_METACLASS_$_EOCTheClass in:
build/something.o
build/something_else.o
duplicate symbol _OBJC_CLASS_$_EOCTheClass in:
build/something.o
build/something_else.o
错误的原因在于,应用程序中的两份代码都实现了EOCTheClass的类。避免此问题的唯一办法变相命名空间。
在使用Cocoa创建应用程序的时候一定要注意,Apple宣称其保留使用所有“两个字母前缀”,所以你自己选用的前缀应该是三个或以上。
第16条:提供“全能初始化方法”
第17条:实现description方法
调试程序时,经常需要打印并查看对象信息。一种办法是编写代码把对象的全部属性都输出到日志中。不过最常用的做法还是像下面这样:
NSLog(@"object =%@",object);
如果object是NSArray那么打印出来的就是数组所有内容,如果object是自定义的类对象,那么结果确是内存地址。除非在自己类里覆写description方法。具体打印什么字段根据需要编写。
NSObject协议中还有个方法要注意那就是debugDescription,用于调试时候的输出。例如:我们在控制台输入po命令,输出:
EOCTest[640:c07] person = <EOCPerson: ox712ad0,"Bob Smith">
请注意,控制台控制台“(EOCPerson*)$1=0x712a4d0”是由调试器添加的,后面的内容才是debugDescription返回的信息,同理,如果你需要在调试的时候打印自定义类的更多信息可以重写debugDescription方法
第18条:尽量使用不可变对象
设计类的时候,应充分运用属性来封装数据。而在使用属性时,则可将其声明为“只读”(read-only)。默认情况下属性“既可读又可写”
● 尽量创建不可变的对象
● 若某属性仅可于对象内部修改,则在“class-continuation分类”中将其由readonly属性扩展为readwrite属性
● 不要把可变的collection作为属性公开,而应提供相关方法,一次修改对象中的可变collection。
第19条:使用清晰而协调的命名方式
NSString这个类就展示了良好的命名习惯。
● +string//用于创建新的空字符串 ● +stringWithString//工厂方法,用于根据某字符串创建新的字符串 ● +localizedStringWithFormat://工厂方法,格式化本地字符串 ● -lowercaseString//把字符串中的大写字母全部转化位小写 ● -intValue//将字符串解析为整数 ● -length//获取字符串长度 ● -lengthOfBytesUsingEncoding://若字符串是以给定的编码格式 ● -getCharacters:range://获取字符串给定范围的字符 ● -(void)getCharacters:(unichar*)buffer range:(NSRange)aRange//首个参数应该指向一个足够大的数组,以便容下请求范围内的那些字符串 ● -hasPrefix://是否有某个前缀 ● -isEqualToString://判断字符串是否相等
第20条:为私有方法名加前缀
第21条:理解Objective-C错误模型
NSError对象封装了三条信息:
● Error domain(错误范围,string)
错误发生的范围。也就是产生错误的根源,通常用一个特有的全局变量来定义。比如说:“处理URL的子系统”在从URL中解析获取得数据时候,如果出错了,那么就会使用NSURLErroeDomain来表示错误范围。
● Error code(错误码,int)
● User info(用户信息,dictionary)
在错误不那么严重的情况下,可以指派“委托方法”来处理错误,也可以把错误信息放到NSError对象里,经由“输出参数”返回给调用者。
第22条:理解NSCopying协议
使用对象时经常需要拷贝它。在Objective-C中,此操作通过copy方法完成。如果想令自己的类支持拷贝操作,那么就要实现NSCopying协议,该协议只有一个方法:
-(id)copyWithZone:(NSZone*)zone
为何会出现NSZone呢?因为以前开发程序时,会根据把内存分成不同的“区”(zone),而对象会创建在某个去里面。现在不用了,每隔程序只有一个区:“默认区”。所以说,尽管去实现这个方法,但是你不必担心其中的zone参数
#import<Foundation/Foundation.h> @interface EOCPerson :NSObject<NSCopying> @propetry (nonatomic,copy,readonly) NSString *firstName; @propetry (nonatomic,copy,readonly) NSString *lastName; -(id) initWithFirstName:(NSString*)firstName andLastName:(NSString*)lastName; -(void)addFriend:(EOCPerson*) person; -(void)removeFriend:(EOCPerson*)person; @end @implementation EOCPerson{ NSMutableSet *_friends; } -(id) initWithFirstName:(NSString*)firstName andLastName:(NSString*)lastName{ if((self = [super init])){ _firstName = [firstName copy]; _lastName = [lastName copy]; _friends = [NSMutableSet new]; } return self } -(void)addFriend:(EOCPerson*)person{ [_friends addObject:person]; } -(void) removeFriend:(EOCPerson*)person{ [_friends removeObject:person]; } -(id)copyWithZone:(NSZone)zone{ EOCPerson *copy =[[[self class]allocWithZone:zone] initWithFirstName:_firstName andLastName:_lastName]; copy->_friends = [_friends mutableCopy]; return copy } @end
copyWithZone实现了1. copy 一份EOCPerson,2. 它把本地的_friends实例变量复制了一份,令copy对象的_friends实例变量指向这个复制过的set。注意,这里使用->语法,因为_friends并非属性。如果_friends是不可变的数组,则不需要复制一份。
在编写拷贝方法时,还要决定一个问题,就是应该“深拷贝”还是“浅拷贝”。深拷贝的意思就是:在拷贝对象自身时,将底层数据也一并复制过去。Foundation框架中的所有collection类在默认情况下都执行钱拷贝,也就是说,只拷贝容器对象本身,而不复制其中的数据。
对于容器类
以NSSet为例,该类提供了下面的初始化方法,用以执行深拷贝
-(id)initWithSet:(NSArray*)array copyItems:(BOOL)copyItems
因为没有专门定义深拷贝的协议,所以其具体执行方式由每个类来决定,你只需要决定自己所写的类是否要提供深拷贝方法即可。
对于非容器类的copy
以NSString为例
// 非容器类对象 NSString *str = @"origin string"; NSString *strCopy = [str copy]; NSMutableString *mstrCopy = [str mutableCopy]; [mstrCopy appendString:@"??"];
str和strCopy指向的是同一块内存区域(浅拷贝),我们称之为弱引用(weak reference)。而mstrCopy是真正的复制(深拷贝)
NSMutableString *mstr = [NSMutableString stringWithString:@"origin"]; NSString *strCopy = [mstr copy]; NSMutableString *mstrCopy = [mstr copy]; NSMutableString *mstrMCopy = [mstr mutableCopy]; //[mstrCopy appendString:@"1111"]; //error [mstr appendString:@"222"]; [mstrMCopy appendString:@"333"];
以上四个对象所分配的内存都是不一样的。而且对于mstrCopy,它所指向的其实是一个imutable对象,是不可改变的,所以会出错。这点要注意,好好理解。
对于非容器类对象,有:
● 如果对一个不可变对象复制,copy是指针复制,即浅拷贝;而mutableCopy则是对象复制,即深拷贝。
● 如果是对可变对象复制,都是深拷贝,但copy复制返回的对象是不可变的。
【本章要点】
● 若想令自己写的对象具有拷贝功能,则需要实现NSCopying协议。
● 如果自定义的对象分为可变版本与不可变版本,那么就要同时实现NSCopying与NSMutableCopying协议。
● 复制对象时需要决定采用浅拷贝还是深拷贝,尽量执行浅拷贝。
● 如果你所写的对象需要深拷贝,那么可以考虑新增一个专门执行深拷贝的方法。
《Effective Objective-C 2.0》—(第15-22条)—接口与API设计、深拷贝、浅拷贝