Effective Objective-C 2.0(四)协议与分类
00 分钟
2019-7-20
2019-7-20
type
status
date
slug
summary
tags
category
icon
password
这是Effective Objective-C 2.0系列的第4篇。

一、最佳实践

  • 在开发中合理巧妙的使用位段
  • 将类的实现代码分散到便于管理的数个分类之中
  • 使用分类机制把类的实现代码划分成易于管理的小模块;
  • 将应该视为“私有”的方法归入名叫Private的分类中,以隐藏实现细节。
  • 总是为第三方类的分类名称添加前缀
  • 向第三方类中添加分类时,总应该给其名称加上你专用的前缀;
  • 向第三方类中添加分类时,总应该给其中的方法加上你专用的前缀。
  • 勿在分类中声明属性
  • 把封装数据所用的全部属性都定义在主接口里;
  • 在“class-continuation”分类之外的其他分类中,可以定义存取方法,但尽量不要定义属性。
编者按:在很多第三方开源库中,使用“关联对象”来在分类中定义属性是很常见的手段。
  • 使用“class-continuation”分类隐藏实现细节
  • 通过“class-continuation”分类向类中新增实例变量;
  • 如果某属性在主接口总声明为“readonly(只读)”,而类的内部又要用设置方法修改此属性,那么就在“class-continuation”中将其扩展为“readwrite(可读写)”;
  • 把私有方法的原型声明声明在“class-continuation”里面;
  • 若想使类所遵循的协议不为人所知,则可与“class-continuation”中声明。
  • 通过协议提供匿名对象
  • 协议可在某某种程度上提供匿名类型。具体的对象类型可以淡化成遵从某些一的id类型,协议里规定了对所应事先的方法。
  • 使用匿名对象来隐藏类型名称(或类名)。
  • 如果具体类型不重要,重要的是对象能够响应(定义在协议里的)特定方法,那么可使用匿名对象来表示。

二、实践详解

2.1 位段

在委托代理中,如果要频繁检查该代理是否响应某个方法,那么将代理相应能力缓存起来达到优化。而优化的最佳途径就使用“位段”。“位段”是一个C语言数据类型。
关于位段,简要做个说明:

2.1.1 定义

或者:

2.1.2 示例

  1. 位段的类型只能是int,unsigned int,signed int三种类型,不能是char型或者浮点型;
  1. 位段占的二进制位数不能超过该基本类型所能表示的最大位数,比如在VC中int是占4个字节,那么最多只能是32位;
  1. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的;
  1. 若位段占的二进制位数为0,则这个位段必须是无名位段,下一个位段从下一个位段存储单元开始存放;

2.1.3 位段在委托代理模式中的应用

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

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

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

分类为现有类添加新功能,假如多个分类都为该类添加了同一个方法名的某一个方法,那么在运行时,会造成该方法名多次覆盖,以最后一次覆盖为主。假如遇到这种情况的bug,很难追溯源头,因为你不知道,其他人也重写了该方法。所以为了避免这种情况的发生,就需要为分类加上前缀,作为一个“命名空间”,比如:
即便加了前缀,也难保其他分类不会覆盖你所写的放方法。但是降低了概率。
  • 向第三方类中添加分类时,总应该给其名称加上你专用的前缀;
  • 向第三方类中添加分类时,总应该给其中的方法加上你专用的前缀。

2.4 勿在分类中声明属性

属性是封装数据的方式。在技术上,分类也可以声明属性,但是要避免这种做法。
声明文件:
实现文件:
这时会发出警告:
要消除警告,要么添加@dynamic,要么添加对应的setter/getter方法。
下面是在实现文件里添加setter/getter方法:
在本例中,正确的做法是将所有的属性都定义在主接口里。主接口是唯一能定义成员变量(数据)的地方。而属性只是定义实例变量及相关存取方法所用的“语法糖”,所以也应遵循同实例变量一样的规则。至于分类机制应将其理解为一种手段,目标在于扩展类的功能,而非封装数据。
  • 把封装数据所用的全部属性都定义在主接口里;
  • 在“class-continuation”分类之外的其他分类中,可以定义存取方法,但尽量不要定义属性。

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

class-continuation和其他分类不同,它必须定义在其所接续的那个类的实现文件里。其重要之处在于,这是唯一能声明实例变量的分类,而且此分类没有特定的实现文件,其中的方法都应定义在类的主实现文件里。
可参考本文上段中“位段在委托代理模式中的应用”中定义的位段,即实例变量。
在class-continuation中定义实例变量,主要是为了将细节隐藏起来。
另外,在class-continuation中声明只有在类的实现代码中的私有方法也是较为可取的。在编写类的实现代码之前,先在class-continuation中将需要实现的方法原型声明,然后逐一实现。比如:
最后,还有一种情况,就是对象所遵循的协议只应视为私有的话,那么最好也在class-continuation中声明。比如:
  • 通过“class-continuation”分类向类中新增实例变量;
  • 如果某属性在主接口总声明为“readonly(只读)”,而类的内部又要用设置方法修改此属性,那么就在“class-continuation”中将其扩展为“readwrite(可读写)”;
  • 把私有方法的原型声明声明在“class-continuation”里面;
  • 若想使类所遵循的协议不为人所知,则可与“class-continuation”中声明。

2.5 通过协议提供匿名对象

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