effective OC2.0 52阅读笔记(三 接口与API设计)

第三章:接口与API设计

15 用前缀避免命名空间冲突 总结:

避免重名符号错误的唯一办法是变相实现命名空间:为所有符号都加上命名前缀。类和分类都应加三字前缀。

总是给C函数的名字加上前缀:类实现文件中的纯C函数及全局变量,是算作“顶级符号的”(不属于任何类)。

为三方库加前缀:如果用第三方库编写自己的代码,并准备将其再发布为程序库供他人使用,尤其要注意符号问题,应该再加上引用的三方库加上自己的三字前缀。(若自己所开发的程序中用到了第三方库,则应为其中的名称加上前缀)

16 提供全能初始化方法 总结:

只有在全能初始化方法中,才会存储内部数据:这样的话,当底层数据存储机制改变时,只需修改此方法的代码就好。

有时候需要编写多个全能初始化方法:譬如某个对象的实例有两种完全不同的创建方式,必须分开处理,例如NSCoding,提供序列化机制,对象可以此指明其自身的编码和解码方式,UI框架就广泛运用此机制,NIB和视图(xml)对象之间的转换。

在类中提供一个全能初始化方法,并于文档里指明其他初始化方法均应调用此方法。

若全能初始化方法与超类不同,则需覆写超类中的对应方法。如果超类的初始化方法不适用于子类,那么应该覆写这个超类方法,并在其中抛出异常。

17 实现description方法 总结:

重写debugDescription方法:po之后打印的就是debugDescription的内容。

18 尽量使用不可变对象 总结:

当属性是即可读又可写的时候设计出来的类都是可变的。应尽量把对外公布出来的属性设为只读,而且只在确有必要时才将属性对外公布。

@property (nonatomic, copy, readonly) NSString *identifier;这个时候不指定内存管理语义也可以,@property (nonatomic, readonly) NSString *identifier;但是注意此时在外边仍然能通过键值编码setValue:forKey设置这些属性值。

不要把可变的collection作为属性公开,而应提供相关方法以此修改对象中的可变collection。

我们建议采纳的安全模式是这样的:从主线程中提取出要使用到的数据,并利用一个操作队列在后台处理相关的数据,最后回到主队列中来发送你在后台队列中得到的结果。使用这种方式,你不需要自己做任何锁操作,这也就大大减少了犯错误的几率。

19 使用清晰而协调的命名方式 总结:

驼峰式大小写命名法:方法和变量名,以小写字母开头,其后每个单词首字母大写。

如果方法的返回值是新创建的,那么方法名的首个词应是返回值的类型,除非前面还有修饰语,属性的存取方法不遵循这种命名方式,因为一般认为这些方法不会创建新对象,即使返回拷贝对象。

方法名里不要使用缩略后的类型名称。

20 为私有方法名加前缀 总结:

私有方法只在实现的时候声明。OC没有办法将方法标为私有。所以给私有方法的名称加上前缀,可以很容易地将其捅公共方法区分开。

苹果用一个下划线作为私有方法的前缀。

不能将方法限定于某个范围内,也许是OC的缺点,然后作为动态方法派发系统这个强大组件的一部分,此特性也带来了诸多好处。

21 理解OC错误模型 总结:

OC对异常的处理方法是,只有在极罕见的情况下抛出异常,异常抛出之后无须考虑恢复问题,而且应用程序此时也应该退出。也就是说,不用再编写复杂的异常安全代码了。

在出现不那么严重的错误时,令方法返回nil/0,或是使用NSError,以表明其中有错误发生。其中NSError的用法更加灵活,因为可以由此对象将错误的原因报告给调用者。

对于NSError有错误发生时

(1)当前对象会把错误信息经由协议中的某个方法传递给其委托对象

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;

(2)或者经由方法的输出参数,返回给调用者

- (BOOL)doSomething:(NSError * __autoreleasing*)error;此方法不仅能有普通的返回值,而且还能经由“输出参数”把NSError对象回传给调用者。

例如:

NSError *error = nil;

BOOL ret = [object doSomething:&error];表示该操作是成功了还是失败了

if(error){具体错误信息

}

如果不需要关注error信息则会传入nil

BOOL ret = [object doSomething:nil];

具体的doSomething实现:

- (BOOL)doSomething:(NSError **)error

{
  //do something that may cause an error

  if(/*there was an erro*/){

    if(error){

      *error = [NSError errorWithDomain:domain code:code userInfo:userInfo];

    }

    return NO;

  }else{

    return YES;

  }

}

*error为error参数解引用,就是说error所指的那个指针现在要指向一个新的NSError对象了。在解引用之前必须先保证error参数不是nil,因为空指针解引用会导致“段错误”并使应用程序崩溃。调用者在不关心具体错误时,会给error参数传入Nil。

22 理解NSCopying协议 总结:

没有专门深拷贝的协议,所以其具体执行方式由每个类来确定。我们只需决定自己所写的类是否要提供深拷贝方法即可。

若想令自己所写的对象具有拷贝功能,则需实现NSCopying协议。如果自定义的对象分为可变版本与不可变版本,那么就要同时实现NSCopying与NSMutableCopying协议。(- (id)copyWithZone:(NSZone *zone);  - (id)mutableCopyWithZone:(NSZone *)zone;)

复制对象时需决定采用浅拷贝还是深拷贝,一般情况下应该尽量执行浅拷贝。如果你所写的对象需要深拷贝,那么可考虑新增 一个专门执行深拷贝的方法。譬如:NSSet的深拷贝方法-(id)initWithSet:(NSArray *)array copyItems:(BOOL)copyItems;根据copyItems来确定是否为深拷贝。

Foundation框架中的所有collection(容器对象类)类在默认情况下都执行前拷贝。

{

  NSString *str1 = @"hey";

     NSMutableString *mutStr2 = [NSMutableString stringWithFormat:@"my"];

    NSArray *arr = [NSArray arrayWithObjects:str1,mutStr2,nil];

    NSArray *arrCopy = [arr copy];

    NSArray *arrMutableCopy = [arr mutableCopy];

    NSLog(@" 不可变对象: %p,%p,%p",arr,arr[0],arr[1]);

    NSLog(@" 不可变对象copy: %p,%p,%p",arrCopy,arrCopy[0],arrCopy[1]);

    NSLog(@" 不可变对象mutableCopy: %p,%p,%p",arrMutableCopy,arrMutableCopy[0],arrMutableCopy[1]);

    

    NSMutableArray *mutArr = [NSMutableArray arrayWithObjects:str1,mutStr2, nil];

    NSArray *mutArrCopy = [mutArr copy];

    NSArray *mutArrMutableCopy = [mutArr mutableCopy];

    NSLog(@" 可变对象: %p,%p,%p",mutArr,mutArr[0],mutArr[1]);

    NSLog(@" 可变对象copy: %p,%p,%p",mutArrCopy,mutArrCopy[0],mutArrCopy[1]);

    NSLog(@" 可变对象mutableCopy: %p,%p,%p",mutArrMutableCopy,mutArrMutableCopy[0],mutArrMutableCopy[1]);

    //copy返回的结果不可变,mutableCopy返回的结果可变

    //由结果可见,以上只是对数组指针意义上的深浅复制,然而并不是真正意义上的深复制,因为数组其中的元素都没有复制

    

    NSArray *arrYDeepCopySoCalled = [[NSArray alloc]initWithArray:arr copyItems:YES];

    NSArray *arrNDeepCopySoCalled = [[NSArray alloc]initWithArray:arr copyItems:NO];

    NSArray *mutArrYDeepCopySoCalled = [[NSMutableArray alloc]initWithArray:mutArr copyItems:YES];

    NSArray *mutArrNDeepCopySoCalled = [[NSMutableArray alloc]initWithArray:mutArr copyItems:NO];

    NSLog(@" yes:%p %p no:%p %p yes:%p %p no:%p %p",arrYDeepCopySoCalled[0],arrYDeepCopySoCalled[1],arrNDeepCopySoCalled[0],arrNDeepCopySoCalled[1],mutArrYDeepCopySoCalled[0],mutArrYDeepCopySoCalled[1],mutArrNDeepCopySoCalled[0],mutArrNDeepCopySoCalled[1]);

    //copyItems为NO的时候等同于之前的结果,数组中的元素都没有复制。

    //copyItems为YES的时候,以上也并不是真正意义上的深复制,然而苹果官方已经将这个定义为深复制。对于可变元素是深拷贝,对于不可变元素依然是指针复制。

    

    NSArray *trueArrYDeepCopySoCalled = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:arr]];

    NSArray *treMutArrNDeepCopySoCalled = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:mutArr]];

    NSLog(@" arr:%p %p mutArr:%p %p ",trueArrYDeepCopySoCalled[0],trueArrYDeepCopySoCalled[1],treMutArrNDeepCopySoCalled[0],treMutArrNDeepCopySoCalled[1]);

    //以上才是真正意义上的深拷贝

}

原文地址:https://www.cnblogs.com/encoreMiao/p/5125178.html