【Effective Objective-C】——协议与分类

文章目录

    • 概述
    • 通过委托与数据源协议进行对象间通信
    • 将类的实现代码分散到便于管理的数个分类之中
    • 总是为第三方类的分类名称加前缀
    • 勿在分类中声明属性
    • 使用“class-continuation分类” 隐藏实现细节
    • 通过协议提供匿名对象

概述

  1. Objective-C语言有一项特性叫做“协议”,它与Java的“接口”类似。因为Objective-C语言不支持多重继承,因而我们把某个类应该实现的方法定义在协议里面。
  2. “分类”也是Objectve-C的一项基本重要语言特性。利用分类机制,无需继承子类即可直接为当前类添加方法,而在其他编程语言中则需通过集成子类来实现。

通过委托与数据源协议进行对象间通信

  1. Objective-C开发者广泛使用一种名为“委托模式”的编程设计模式来实现对象间的通信。该模式的主旨是:定义一套接口,某对象若想接受另一个对象的委托,则需遵从此借口,以便成为其“委托对象”。 而这另一个对象则可以给其委托对象回传一些信息,也可以在发生相关事件时通知其委托对象。
    举例:
    假设我们要编写一个从网上获取数据的类,使用委托模式实现:获取网络数据的类中含有一个“委托对象”,在获取完数据之后,它会回调这个委托对象。用下图来表示此概念:
    【Effective Objective-C】——协议与分类_第1张图片
    EOCDataModel 对象就是EOCNetworkFetcher的委托对象。EOCDataModel请求 EOCNetworkFetcher“以异步方式执行一项任务”(perform a task asynchronously),而EOCNetworkFetcher在执行完这项任务之后,就会通知其委托对象,也就是EOCDataModel。
    利用协议机制,用代码来实现协议的定义如下:
@protocol EOCNetworkFetcherDelegate
- (void)networkFetcher:(EOCNetworkFetcher*)fetcher
		didReceiveData:(NSData*)data;
- (void)networkFetcher:(EOCNetworkFetcher*)fetcher 
		didFailWithError:(NSError*)error;

有了这个协议后,类就可以用一个属性来存放其委托对象,在本例中,此类的接口就可以这样写:

@interface EOCNetworkFetcher : NSObject
@property (nonatomic, weak) id <EOCNetworkFetcherDelegate> delegate;

注意:这个属性一定要定义为weak,而非strong,因为两者之间必须为“非拥有关系”;假如在生命属性时用strong将本对象与委托对象之间定为“拥有关系”,那么就会引入“保留环”。也可以定义为unsafe_unretained二者区别为:如果需要在相关对象销毁时自动清空,则定义为前者,若不需要清空,则定义为后者。

实现委托对象的方法是声明某个类遵从委托协议,然后把协议中想实现的那些方法在类里实现出。 根据需要可以选择在何处声明方法:

  • 如果这个协议是委托协议,则通常在类的内部使用,这种情况下就在“class- continuation分类”中声明。
  • 若要向外界公布,那么就在接口中声明。
  1. 两条信息流:
    (1)数据源模式信息从数据源流向类。
    (2)常规的委托模式:信息从类流向委托者。
    【Effective Objective-C】——协议与分类_第2张图片
  2. 将方法响应能力缓冲起来的最佳途径是使用位段数据类型。

要点:

  • 委托模式为对象提供了一套接口,使其可由此相关事件告知其他对象。
  • 将委托对象应该支持的接口定义成协议,在协议中把可能需要处理的事件定义成方法。
  • 当某对象需要从另外一个对象中获取数据时,可以使用委托模式。这种情境下,该模式也叫做“数据源模式”。
  • 若有必要,可实现含有位段的结构体,将委托对象是否能响应相关协议方法这一信息缓冲至其中。

将类的实现代码分散到便于管理的数个分类之中

其实实现这个方法,用到的就是分类的思想,将所有的方法分类,定义在不同的类中,可以把这些类都定义在一个接口文件中(不推荐),最好是把每个分类提取到各自的文件中去。

  1. 通过分类机制可以把类代码分成很多易于管理的小块。在使用分类中的方法时,要记得引入头文件。
  2. 之所以要将类代码打散到分类中还有个原因就是便于调试,可以根据回溯信息中的分类名称精确定位到类中方法所属的功能区。
  3. 可创建Private分类,将不公开的方法属性放入此类。

要点:

  • 使用分类机制把类中的实现代码划分为易于管理的小块。
  • 将应该视为“私有”的方法归入名叫Private的分类中,以隐藏细节。

总是为第三方类的分类名称加前缀

在使用分类向无源码的既有类中新增功能时,为了避免新增功能的方法名称和已有的方法名称一样而导致的方法被重写,所以一般做法都是:以命名空间来区别各个分类的名称与其中所定义的方法,给相关名称加上某个共用的前缀(这个前缀应该与应用程序或应用程序库中其他地方所用的前缀相同)。

要点:

  • 向第三方类中添加分类时,总应给其名称前加上你专用的前缀。
  • 向第三方类中添加分类时,总应给其中的方法名加上你专用的前缀。

勿在分类中声明属性

属性是封装数据的一种方式,除了“class-continuation分类”之外,其它分类都无法向类中新增实例变量,因此无法把实现属性所需要的实例变量合成出来。

  1. 关联对象可以解决在分类中不能合成实例变量的问题。
    【Effective Objective-C】——协议与分类_第3张图片
    但是这样不太理想,因为要把相同的代码写好多遍。
    正确的做法应该是:把所有属性定义在主接口里,这里是唯一能够定义实例变量的地方。

  2. 由于获取方法不访问数据,而且属性也不需要有实例变量来实现,所以直接写一个方法来返回自己想要的数据。

要点:

  • 把封装数据所用的全部属性都定义在主接口里
  • 在“class-continuation分类”之外的其他分类中,可以定义为存取方法,但尽量不要定义属性。

使用“class-continuation分类” 隐藏实现细节

  1. class-continuation分类”是唯一能声明实例变量的分类,而且此分类没有特定的实现文件,其中方法都应该定义在类的主实现文件里,而且这个分类没有名字,比如有个类叫做EOCPerson,则其“class-continuation分类”写法如下:
@interface EOCPerson ()
//Methods here
@end
  1. 这种分类可以定义方法和实例变量,因为有“稳固的ABI机制”,使得我们无需知道对象大小即可使用它。这样我们就可以通过“class-continuation分类”给类新增实例变量了:
    【Effective Objective-C】——协议与分类_第4张图片

  2. class-continuation分类”还有一种合理用法,就是将public接口中声明为“只读”的属性扩展为“可读写”,以便在类的内部设置其值。

  3. 若对象遵从的协议只应视为私有,则可在“class-continuation分类”中声明。
    要点

  • 通过“class-continuation分类”向类中新增实例变量。
  • 如果某属性在主接口中声明为“只读”,而类的内部又要用设置方法修改此属性,要么就在“class-continuation分类”中将其扩展为“可读写”。
  • 把私有方法的原型声明在“class-continuation分类”里面。
  • 若想使类所遵循的协议不为人所知,则可于“class-continuation分类”中声明。

通过协议提供匿名对象

匿名对象 :用协议把自己所写的API之中的实现细节隐藏起来,将返回的对象设计为遵从此协议的纯id类型。例如:

@property (nonatomic, weak) id <EOCDelegate> delegate;

对于具备此属性的类来说,delegate就是“匿名的”。

要点

  • 协议可在某种程度上提供匿名类型,具体的对象类型可以淡化成遵从某协议的id类型,协议里规定了对象所应实现的方法。
  • 使用匿名对象来隐藏类型名称(或类名)。
  • 如果具体类型不重要,重要的是对象能够响应(定义在协议里的)特定方法,那么可使用匿名对象来表示。

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