《Objective-C编程全解》 读书笔记 第十章 范畴



范畴
范畴:
实现某个类的一部分方法的模块叫作范畴或类别。

范畴和类一样,都是在接口文件中声明,在类文件中实现。但范畴中不能声明实例变量,只能声明方法,声明的方法既可以是类方法,也可以是实例方法。

范畴的语法如下所示。“类名”部分为范畴所属的类的名字或即将添加该范畴的类的名字。“类名”必须已经存在的类,不能为一个不存在的类定义范畴:
//范畴的声明
@interface 类名 (范畴名)


方法的声明;

  ...

@end



// 范畴的实现
@implementation 类名 (范畴名)
方法的定义;

  ...

@end

范畴和文件的组织:
包含主文件接口的头文件中也包含了其他所有接口:



另外一种定义范畴的方法就是将每个范畴部分都单独定义成一个头文件,每个实现文件都可以被单独编译。实现文件一般被命名为:“类名+范畴名.m”


范畴的接口部分需要遵循以下几个原则:
  • 范畴的接口部分必须引用主文件的接口文件
  • 范畴的实现部分必须引用对应的接口文件
  • 使用范畴中的方法时必须引用这个方法所在的头文件

作为子模块的范畴:
上面讲述的内容的前提都是类的规模非常大,这种情况下通过使用范畴可以提高开发效率,但实际上规模大的类并不是一个好的选择。在设计阶段,如果发现一个类的方法非常多,最好把这个类的功能分成几个类来实现。

方法的前向声明:
未在接口部分中声明的局部方法,和未声明的C语言函数一样,其定义之前的代码是没法使用他们的。而通过使用范畴,就可以解决这个问题。类似于C语言中通过在文件的最前面加上函数声明来解决类似的问题。

把局部方法都加入到一个局部的范畴中,并在实现文件的头部加上范畴的声明和定义。

私有方法:

将类划分多个范畴之后,就必须考虑如何划分共有方法。

如图,划分方法是创建一个对外不公开的接口文件,其中声明的类的共有变量和方法。像这样,即使在多个文件中进行了共同的声明,也可以定义仅供类内文件使用而不对类外公开的方法和实例变量。这种方法叫私有方法。

为避免子类将父类的私有方法名覆盖,推荐为私有方法名加上固定前缀。

类扩展:
把类分为多个范畴来实现的情况下,主类和各个范畴都是独立的,每个范畴都不清楚其他的部分。有的范畴可能是执行前加载的,有的范畴可能是执行时动态加载的。这种实现方法的可扩展性非常好,但编译器在链接时不会检查是否所有的范畴都被链接到了可执行文件中。针对这种情况,引入了类扩展的概念。

类扩展的声明和范畴比较相似,只是圆括号之间没有文本。类扩展中也可以增加实例变量:
@interface
Card () {
  BOOL flag;
}
- ( BOOL)hasSameSuit:(Card *)obj;

@end

类扩展中声明的方法需要在类的实现文件中实现,不管是否引入了类扩展的定义,只要在类的实现部分中没实现对应的方法,就会出现编译错误。也就是说,通过将一定要实现的私有方法放入到类扩展中,就可以防止忘记实现这个方法或漏掉方法的实现等问题。

但是,类扩展自身并不会让方法变为私有方法,如果想让方法变成私有方法,必须不能让包含了类扩展的头文件对外公开。

类扩展中声明的变量只能在引入了类的主接口和扩展声明的范畴中使用。主类的实现部分也可以声明实例变量,但实例变量只在该文件(实现文件)中有效。与此相对,类扩展中定义的实例变量可以在多个范畴中使用。


范畴和属性声明:
范畴的接口中可以包含属性声明,但范畴的实现部分不能包含@synthesize。实现部分需手动定义访问方法。这是为了防止随意访问同一个类的不同文件中定义的实例变量。

类扩展中也可以包含属性声明,属性是通过在类的实现部分包含@synthesize或者属性方法来实现的。类扩展中也可以声明实例变量的属性。

类扩展只能引用类内的实例或方法,至于范畴的头文件是公开还是仅限类内使用,则是由具体的使用情况决定的。

  实例变量 方法声明 @synthesize
主类.h公开公开-
类扩展.h类内类内-
主类.m文件内文件内o
范畴.hX公开/类内-
范畴.mX文件内X



给现有类追加范畴
追加新的方法:
无论是自己定义的类还是系统的类,利用范畴都可以为已有类追加新方法。

利用范畴只可以追加方法,不可以追加实例变量。

我们一般通过继承的方式为现有类增加新的功能,但有时候使用子类并不方便,例如无法用通常的方法来为NSString创建子类等。而范畴的好处就是可以为正在使用的类增加新功能。

虽然范畴追加方法很方便,但也不要滥用。

覆盖已有方法:
新定义的范畴中的方法如果和原有方法重名的话,新定义的方法就会覆盖老方法。但是不建议利用范畴覆盖已有的方法。


关联引用
关联引用的概念:
通过范畴可以为一个类追加新的方法但不能追加实例变量。但是,利用Objective-C的动态性,并借助运行时(runtime)的功能,就可以为已存在的实例对象增加实例变量,这个功能叫做关联引用(associative references)。将这个功能和范畴组合在一起使用,即使不创建子类,也能够对类进行动态扩展。

添加关联和检索关联:
这两个方法定义在objc/runtime.h中。
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)

id objc_getAssociatedObject(id object, void *key)

在本章中,把要增加关联的对象(想扩展的对象)称为所有者,把追加的对象称为引用对象。key的值必须使用确定的,不再改变的地址作为键值。使用地址作为key的原因是为了保证唯一性,key不会被用来存储什么,也不会对其进行赋值操作。

对象的存储方法:
objc_setAssociatedObject的第四个参数policy是关联策略。关联策略表明了关联对象是通过何种方式进行关联的,以及这种关联是原子的还是非原子的。policy的值有以下几种可供选择,其中最常用的是OBJC_ASSOCIATION_RETAIN.

OBJC_ASSOCIATION_ASSIGN
基于引用计数的内存管理时,不会给关联对象发送retain,仅仅通过赋值进行关联。内存管理使用垃圾回收时,会把引用对象作为弱引用保存。

OBJC_ASSOCIATION_RETAIN_NONATOMIC
使用基于引用计数的内存管理时,会给关联对象发送retain消息并保持,如果同样的key已经关联到其他对象,则会给其他对象发送release。释放关联对象的所有者时,会给所有的关联对象发送release消息。内存使用垃圾回收时,会以强引用的形式保存关联对象。

OBJC_ASSOCIATION_RETAIN
功能和上一个一样,唯一的区别是OBJC_ASSOCIATION_RETAIN是多线程安全的,支持排他性关联操作。

OBJC_ASSOCIATION_COPY_NONATOMIC
在进行对象关联的时候会复制一份原对象,并用新复制的对象进行关联操作。

OBJC_ASSOCIATION_COPY
功能和上一个一样,唯一的区别是OBJC_ASSOCIATION_COPY时多线程安全的,支持排他性的关联操作。

断开关联:
void objc_removeAssociatedObjects(id object)

通过范畴扩展添加的实例变量并不是真正的实例变量,所以对象复制和归档要特别注意。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值