Effective Objective-C(第15-22条)接口与API设计、深拷贝、浅拷贝

来源:互联网 发布:360全景系统源码 编辑:程序博客网 时间:2024/05/01 03:52

第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协议。
● 复制对象时需要决定采用浅拷贝还是深拷贝,尽量执行浅拷贝。
● 如果你所写的对象需要深拷贝,那么可以考虑新增一个专门执行深拷贝的方法。

0 0
原创粉丝点击