【Effective Objective-C】——接口与API设计

文章目录

    • 概述
    • 用前缀避免命名空间重复
    • 提供“”全能初始化方法”
    • 实现description方法
    • 尽量使用不可变对象
    • 使用清晰而协调的命名方式
    • 为私有方法名加前缀
    • 理解Objective-C错误模型
    • 理解NSCopying协议

概述

开源社区以及开源组件随着iOS开发而流行起来,所以我们 经常会在开发自己的应用程序时使用他人的代码。所以我们要把代码写的清晰一些,以便其他开发者能够迅速而方便的将其集成到他们的项目里。

用前缀避免命名空间重复

Object-C没有其他语言那种内置的命名空间机制,所以我们在起名时要设法避免潜在的命名冲突。如果发生命名冲突,那么应用程序的链接过程就会出错。

  1. 比无法链接更糟糕的情况是,在运行期载入了含有重名类的程序库。此时,“动态加载器”就遭遇了“重名符号错误”,很可能令整个程序崩溃。
  2. 避免命名空间重复的唯一办法就是实现命名空间:为所有名称都加上适当的前缀
  3. 使用Cocoa创建应用程序时一定要注意,Apple宣称其保留使用所有“两字母前缀”的权利,所以你自己选用的应该是三个字母的。

要点:

  • 选择与你的公司、应用程序或者二者皆有关联之名称作为类名的前缀,并在所有代码中均使用这一前缀。
  • 若自己所开发的程序中用到了第三方库,则应为其中的名称加上前面的前缀。

提供“”全能初始化方法”

  1. 所有对象都要初始化。
  2. 在初始化为对象提供必要信息以能完成工作的初始化方法叫做“全能初始化方法”。
  3. 每个子类的全能初始化方法都应该调用其超类的对应方法,并逐层向上。

要点:

  • 在类中提供一个全能初始化方法,并与文档里指明。其他初始化方法均应调用此方法。
  • 若全能初始化方法与超类不同,则需要重写超类中对应方法。
  • 如果超类的初始化方法不适用于子类,那么应该重写这个超类方法,并在其中抛出异常。

实现description方法

  1. 在构建需要打印到日志的字符串时,object对象会受到description消息,该方法返回的描述信息将取代“格式字符串”里的“%@”。
  2. 根据自己的需求去重写description方法。
  3. 在自定义的description方法中,把待打印的信息放到字典里面,然后将字典对象的description方法所输出的内容包含在字符串里并返回,这样就可以实现精简的信息输出方式。
  4. debugDescription方法是开发者在调试器中以控制台命令打印对象时才调用。
  5. 在程序运行到断点时,开发者可以向调试控制台里输入命令了,LLDB的“po”命令可以完成的对象的打印工作。

要点:

  • 实现description方法返回一个有意义的字符串,用以描述该实例
  • 若想在调试时打印出更详尽的对象描述信息,则应实现debugDescription

尽量使用不可变对象

  1. 把可变对象放入collection之后又修改其内容,那么很容易破坏set的内部数据结构,使其使其固有的语义。
  2. 在编程中,应该尽量把对外公布的属性设为只读,而且只在确有必要时才将属性对外公布。

要点:

  • 尽量创建不可变的对象
  • 若某属性仅可于对象内部修改,则在“class-continuation分类”中将其由readonly属性扩展为readwrite属性。
  • 不要把可变的collection作为属性公开,而应提供相关方法,以此修改对象中的可变collection

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

  1. Objective-C里方法和变量名使用“驼峰式大小写命名法”——以小写字母开头,其后每一个单词的首字母大写。 类名也用驼峰命名法,不过其首字母要大写,而且前面还有三个前缀首字母。
  2. 使用长名字可令代码更为易读,能够清楚的知道代码的用途。
  3. 方法命名时的几条注意事项:
    ■如果方法的返回值是新创建的,那么方法名的首个词应是返回值的类型,除非前面还有修饰语,例如localizedString。属性的存取方法不遵循这种命名方式,因为一般认为这些方法不会创建新对象,即便有时返回内部对象的一份拷贝,我们也认为那相当于原有的对象。这些存取方法应该按照其所对应的属性来命名。
    ■应该把表示参数类型的名词放在参数前面。
    ■如果方法要在当前对象上执行操作,那么就应该包含动词;若执行操作时还需要参数,则应该在动词后面加上一个或多个名词。
    ■不要使用str这种简称,应该用string这样的全称。
    Boolean属性应加is前缀。如果某方法返回非属性的Boolean值,那么应该根据其功能,选用hasis当前缀。
    ■将get这个前缀留给那些借由“输出参数”来保存返回值的方法,比如说,把返回值填充到“C语言式数组" (C-style array)里的那种方法就可以使用这个词做前缀。
  4. 应该为类与协议的名称前加上前缀,以避免命名空间冲突且应该像给方法起名时那样把词句组织好,使其从左向右读起来通顺。
  5. 创建自定义的委托协议,则其名称中应该包含委托发起方的名称,后面再跟上delegate

要点:

  1. 起名时应遵从标准的Objective-C命名规范,这样创建出来的接口更容易为开发者所理解。
  2. 方法名要言简意赅,从左至右读起来要像个日常用语中的句子才好。
  3. 方法名里不要使用缩略后的类型名称。
  4. 给方法起名时的第一 要务就是确保其风格与你自己的代码或所要集成的框架相符。

为私有方法名加前缀

  1. 编写类的实现代码时,经常要写一些只在内部使用的方法,应该为这种方法名称加上某些前缀。
  2. 与公共方法不同,私有方法不出现在接口定义中,。有时要在“class-continuation分类”里声明私有方法,然而最近修订的编译器不要求在使用方法前必须先行声明了,所以说,私有方法一般只在实现的时候声明
  3. 在Objective-C中,没办法把方法标为私有化,每个对象都可以响应任意消息,而且可以在运行期检视某个对象所能直接响应的消息
  4. 以避免重写苹果公司提供的私有化方法,所以不要用下划线做前缀。

要点:

  1. 给私有方法的名称加上前缀,这样可以很容易地将其同公共方法区分开。
  2. 不要单用一个下划线作私有方法的前缀,因为这种方法是预留给苹果公司用的。

理解Objective-C错误模型

  1. “自动引用计数”在默认情况下不是“异常安全的”,具体来说,这意味着:如果抛出异常,那么本应在作用域末尾释放的对象现在却不会自动释放了。如果想生成“异常安全”的代码,可以通过设置编译器的标志来实现,不过这将引入一些额外代码,在不抛出异常之前,也照样要执行这部分代码,需要打开的编译器标志叫做-fobjc-are-exceptions
  2. Objective-C语言所采用的方法是是:只在及其罕见的情况下抛出异常,异常抛出之后,无须考虑恢复问题,而且这时应用程序也应该退出了,所以也就不用写“异常安全代码”了。
  3. 异常只用于处理严重错误,在出现其他不那么严重的错误时,Objective-C语言所用的编程范式为:令方法返回nil/0,或是使用NSError,以表明其中有错误发生。
  4. NSError对象里封装了三条消息:
    【Effective Objective-C】——接口与API设计_第1张图片
  5. 在设计API的时候,NSError的第一种常见的用法是通过委托协议来传递此错误,有错误发生时,当前对象会把错误信息经由协议中的某个方法传给其委托对象。
  6. NSError的另外一种常见用法是:经由方法的“输出参数”返回给调用者。
  7. error参数“解引用(error所指的那个指针现在要指向一个新的NSError对象)”之前,必须先保证error参数不是nil,因为空指针解引用会导致段错误并使程序崩溃,所以一定要判断error是不是nil

理解NSCopying协议

  1. 如果想令自己的类支持拷贝操作,那就要实现NSCopying协议,该协议只有一个方法:
- (id)copyWithZone:(NSZone*)zone

为何会出现NSZone呢?因为以前开发程序时,会据此把内存分成不同的“区(zone)”,而对象会创建在某个区里面。但是现在只有一个去:“默认区”。所以只需实现这个方法不必去考虑其中的zone函数。
2.对于不可变的NSArray与可变的NSMutableArray来说,下列关系总是成立的:

[NSMutableArray copy] => NSArray
[NSArray mutableCopy] => NSMutableArray

注意:在可变对象上调用copy方法会返回一个不可变类的实例,这样做是为了能在可变版本与不可变版本之间自由切换。还有一种方法,就是提供三个方法:copy、immutableCopy、mutableCopy,其中copy所返回的拷贝对象与当前对象的类型一致,而另外两个方法则分别返回不可变版本与可变版本的拷贝。
2. 在编写拷贝方法时,要注意一个问题就是要执行“深拷贝”还是“浅拷贝”。
深拷贝:在拷贝对象自身时,将其底层数据也一并复制过去。也就是在堆里新开辟一块内存存放底层数据
浅拷贝:只是拷贝容器对象本身,而不复制其中数据,也就是只拷贝存放在栈上指向堆内存的指针
【Effective Objective-C】——接口与API设计_第2张图片
当如果有必要使用深拷贝时,也可以增加一个执行深拷贝的方法,以NSSet为例,该类提供了一个下面初始化方法:

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

copyItem参数设为YES,则该方法会向数组中的每个元素发送copy消息,用拷贝好的元素创建新的set,并将其返回给调用者。.
3. 不要假定遵从了NSCopying协议的对象都会执行深拷贝,在绝大多数情况下,执行的都是浅拷贝。

要点:

  • 若想令自己所写的对象具有拷贝功能,则需实现NSCopying协议。
  • 如果自定义的对象分为可变版本与不可变版本,那么就要同时实现NSCopyingNSMutableCopying 协议。
  • 复制对象时需决定采用浅拷贝还是深拷贝,一般情况下应该尽量执行浅拷贝。如果你所写的对象需要深拷贝, 那么可考虑新增一个专门执行深拷贝 的方法。

你可能感兴趣的:(OC,objective-c,开发语言,macos)