有时候我们需要对象没有访问权限,但是子类有访问权限的设计,在C++里,有个protected限制符,可以解决这个问题。其实OC也有这个,@protected (@interface 成员变量默认的修饰符),但是OC有个限制,就是不能加入方法,只能使用成员变量。
当然,往这个方向发散下去,使用组合也是能解决问题的,例如
这个其实也是用了C++的方式去解决。
有没有办法用OC的方案去解决这个问题,答案是肯定的。首先,我们先明确两个点,第一:头文件的定义与实现没有太大的关系,编译器在连接的时候主要是对实现文件连接,所以说你头文件在那定义,以及实现文件在那,没有关联性,你可以定义在A.h,然后在B.m去实现它。第二:interface 关键字一直都是接口(不是类,Go语言也是这样的)的意思。基于这两点, 我们可以定义一个私有文件,然后在这个文件里声明接口,并且只在另外一个文件.m里面去实现(对外面是隐藏的),然后在他的子类里面去,基于这个想法,有三种方案1.category(也称为非正式协议),2.extension
(匿名类别或者类扩展),3.protocol(duck type)
1.category
注意点:
如果多个相同源类有相同的方法名或者跟基类的方法名一样,会导致不可预知的问题。事实上,category的方法并没有“完全替换掉”原来类已经有的方法,也就是说如果category和原来类都有methodA,那么category附加完成之后,类的方法列表里会有两个methodA,category的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的后面,这也就是我们平常所说的category的方法会“覆盖”掉原来类的同名方法,这是因为运行时在查找方法的时候是顺着方法列表的顺序查找的,它只要一找到对应名字的方法,就会return,后面可能还有一样名字的方法。有兴趣的可以看下objc-runtime-new.m的代码(开源的)。
2.protocol
3.extension
注意点:
extension看起来很像一个匿名的category,但是extension和有名字的category几乎完全是两个东西。 extension在编译期决议,它就是类的一部分,在编译期和头文件里的@interface以及实现文件里的@implement一起形成一个完整的类,它伴随类的产生而产生,亦随之一起消亡。extension一般用来隐藏类的私有信息,你必须有一个类的源码才能为一个类添加extension,所以你无法为系统的类比如NSString添加extension。但是category则完全不一样,它是在运行期决议的。就category和extension的区别来看,我们可以推导出一个明显的事实,extension可以添加实例变量,而category是无法添加实例变量的(因为在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,这对编译型语言来说是灾难性的)。
从以上三个方案可以得出:如果你有属性的话,category 和 potocol 都需要你自己去实现setter and getter ,而extension 就不需要。而且你如果基类,没有实现方法,是没有警告的,如果子类去调用没有实现的方法就会崩溃 。而且extension 相当于编译的时候已经把他加入到他相应的类里,如果方法没有实现,xcode 是有警告的。综上所述,extension 是最完美的解决方案。