接口与 API 设计

Tuesday, January 30, 2018

用前缀避免命名空间冲突

  • 为所有类名加上前缀,并且是最好三个字母的,两个字母的前缀是 Apple 占用了。
  • 所有名称都应该加上前缀。
  • 如果给既有类增加分类,那么一定要给分类及分类的方法加上前缀。
  • 用第三方库编写自己的代码,应该给自己所用的那份第三方库代码加上自己的前缀。

提供全能初始化方法

  • 在某些类初始化可能需要一些必要的信息,这种情况下需要使用全能初始化方法。
  • 如果创建类实例的方法不止一种,那么这个类就会提供多个初始化方法,这时需要一个作为全能初始化方法,令其它初始化方法都调用它。
  • 在使用了全能初始化方法后, 还是有可能会被调用 init 方法,此时需要重写 init 方法, 给定一个初始化的值或者直接抛出异常。
  • 如果涉及到子类的全能初始化方法,在某些情况下,要覆写超类的全能初始化方法,避免错误。比如正方形是矩形的子类,正方形需要覆写超类的初始化方法。
  • 每个子类的全能初始化方法都应该调用其超类的对应方法,并逐层向上,实现 initWithCoder 时也要这样。

实现 description 方法

  • 如果是自定义类,除非在自己的类里覆写 description 方法,否则打印信息会调用 NSObject 类的默认方法。
  • 在实现 description 方法,建议打印出类的名字和指针地址。
  • 打印数据时,可以利用 NSDictionary 来打印。
  • debugDescription 是在开发者在调试器中以控制台命令打印才调用。

尽量使用不可变对象

  • 应充分利用属性来封装数据,并将其声明为只读的。既可读又可写,这样设计出来的都是可变的。一般建立的数据未必需要可变。
  • 如果把可变对象放入 collection 之后又修改其内容,很容易破坏 set 的内部数据结构,使其失去固有的语义。 -如果想修改封装在内部的数据,但不想令这些数据为外人所动,常用的做法是在内部将 readonly 重新声明为 readwrite。
  • 不要把可变的 collection 作为属性公开,而应该提供相关的方法,以此修改对象中的可变 collection。

使用清晰而协调的命名方式

  • 以驼峰式大小命名

    - (id)initWithWidth:(float)width andHeight:(float)height;

为私有方法名加前缀

  • 为私有方法加上前缀,有助于调试,因为很容易把私有方法与公有方法区分开。
  • 建议使用 p_ 作为前缀。
  • 不要单独以一个_作为前缀,可能会无意中覆写了父类方法。

理解 Objective-C 错误类型

  • 异常抛出只应用于极其严重的错误。

    [NSException exceptionWithName: NSInternalInconsistencyExceptionreason: reason userInfo: nil];

  • 如果非致命错误,应该返回0/nil 或者 NSError。

NSError:

  1. Error domain(错误范围,类型为字符串)
  2. Error code (错误码)
  3. User info (用户信息,其类型为字典)

理解 NSCopying 协议

  • 如果想令自己的类支持拷贝操作,那就要实现 NSCopying 协议,该协议只有一个方法。

    - (id)copyWithZone:(NSZone *)zone

  • copy 方法由 NSObject 实现,该方法只是以『默认区』为参数来调用 copyWithZone。

    - (id)copyWithZone:(NSZone *)zone { EOCPerson *copy = [[self class] allocWithZone: zone] initWithFirstName: _firstName andLastName: _lastName]; return copy; }

  • 如果包含一些数组需要拷贝,则用下列形式实现,假如需要拷贝实例变量,则用->语法。

    - (id)copyWithZone:(NSZone *)zone { EOCPerson *copy = [[self class] allocWithZone: zone] initWithFirstName: _firstName andLastName: _lastName]; copy->_friends = [_friends mutableCopy]; return copy; }

  • mutableCopy 方法来自 NSMutableCopying 协议,该协议也定义了一个方法。

    - (id)mutableCopyWithZone:(NSZone *)zone

-无论当前实例是否可变,若需要获得可变版本的拷贝,均应调用 mutableCopy 方法,若需要不可变拷贝,则应通过 copy 方法来获取。

<code class="language-objectivec">- [NSMutableArray copy] => NSArray
- [NSArray mutableCopy] => NSMutableArray
</code>
  • 默认的 collection 类在默认情况下都执行浅拷贝。只拷贝容易对象本身,不复制其中数据。 image.png

    - (id)initWithSet:(NSArray *)array copyItems:(BOOL)copyItems;

  • 若 copyItem 参数设为 YES,则会像数组中的每个对象发送 copy 消息,用拷贝好的元素创建新的 set。

    - (id)deepCopy { EOCPerson *copy = [[self class] allocWithZone: zone] initWithFirstName: _firstName andLastName: _lastName]; copy->_friends = [[NSMutableSet alloc] initWithSet:_friends copyItem:YES];

       return copy;
    

    }

  • 除非说明是用深拷贝实现 NSCopying 协议的,否则大多数情况都是浅拷贝。

Objective-C

协议与分类

理解"类对象"的用意