分类与扩展

Posted by CoderLeonidas on September 22, 2017

分类(cagegory)

cagegory可以叫做分类或者类别,是Objective-C 2.0之后添加的语言特性。

主要特点及作用:

  • 有名字,一般根据具体作用来取名,如NSAarry(Safe)
  • 一般以独立文件形式存在,也可以放在本类中
  • 如果多个分类中都有和原有类中同名的方法, 那么调用该方法的时候执行谁由编译器决定;编译器会执行最后一个参与编译的分类中的方法。
  • 同名分类方法生效取决于编译顺序
  • 运行时决议
  • 可以模拟多继承
  • 可以给本类扩展方法

    (避免使用继承,便于代码复用)

  • 可以用于分解体积庞大的类文件
  • 可以创建对私有方法的前向引用

本类.m中实现私有方法-methodA,.h中不需要申明,在该类的Category的.h中声明-methodA。在要调用-methodA的类import这个Category,再去运行,就不会报错,而且-methodA的私有方法也执行了

  • 可以添加非正式协议

    (所谓的非正式协议就是类别,即凡是NSObject或其子类的类别,都是非正式协议。因为几乎所有的OC的类都是NSObject的子类,所以所有的类只要import了这个头文件,就可以调用testMethod,这就是所谓的非正式协议)

  • 可以添加属性@property,但不会自动生成getter/setter

    为该属性添加@dynamic可以自动生成该属性的getter/setter,但依然没有合成成员变量

    可通过关联对象(objc_getAssociatedObject\objc_setAssociatedObject)来合成一个成员变量,但这种做法要尽量避免。原因就在于分类无法自己合成成员变量。使用关联对象的方式,代码量大,且在内存管理上容易出错。正确的做法是把属性放在主类中。分类的目的是在于扩展功能,而非封装数据。

    关联对象由AssociationManager管理并在AssociationHashMap存储。所有对象的关联内容都存在用一个全局容器中,和宿主类无关。


/** 
 * Returns the value associated with a given object for a given key.
 * 
 * @param object The source object for the association.
 * @param key The key for the association.
 * 
 * @return The value associated with the key \e key for \e object.
 * 
 * @see objc_setAssociatedObject
 */
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
    OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
1
2
3
4
5
6
7
8
9
10
11
12
13
/** 
 * Sets an associated value for a given object using a given key and association policy.
 * 
 * @param object The source object for the association.
 * @param key The key for the association.
 * @param value The value to associate with the key key for object. Pass nil to clear an existing association.
 * @param policy The policy for the association. For possible values, see “Associative Object Behaviors.”
 * 
 * @see objc_setAssociatedObject
 * @see objc_removeAssociatedObjects
 */
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
    OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
  • 可以将Framework中的私有方法公开

    (Framework私有方法 看不见声明调用不了 如果知道可以通过分类声明这个方法暴露出来;然而苹果对私有API管制较严,所以要根据实际情况慎用)

同名方法的调用顺序: 1.分类 2.本类 3.父类

扩展(extension)

extension其实也是分类的一种,只是没有名字,所以也可以叫做匿名分类。 主要特点及作用:

  • 没有名字,如AClass()
  • 一般存在于本类中,没有独立的文件
  • 可以声名私有属性、私有方法、(私有)成员变量
  • 编译时决议

    必须有一个类的源码才能为一个类添加extension,所以你无法为系统的类比如NSString添加extension

两者区别

  • 分类中原则上只能增加方法;扩展中可以增加方法、属性、实例变量
  • 分类有名字;扩展没名字
  • 分类可以有独立的文件(独立的实现);扩展一般是和主类存在同个类文件中的
  • 分类中的方法实现在运行时决议;类扩展的方法在编译时决议

    扩展是在编译阶段被添加到类中,而分类是在运行时添加到类中。

  • 扩展中声明的方法没被实现,编译器会报;但是分类中的方法没被实现编译器是不会有任何警告的。

可以分别用来做什么

分类:

  • 给类扩展方法
  • 分解体积庞大的类文件
  • 模拟多继承
  • 把Framework私有方法公开
  • 声明私有方法

扩展:

  • 声明私有属性\方法\成员变量

分类有哪些局限性

分类:

  • 不能声名属性
  • 名称冲突,即当类别中的方法与原始类方法名称冲突时,类别具有更高的优先级。
  • 如果多个分类中都有和原有类中同名的方法, 那么调用该方法的时候执行谁由编译器决定;编译器会执行最后一个参与编译的分类中的方法

扩展:

  • 不能为系统类添加扩展

分类的结构体里有哪些成员

1
2
3
4
5
6
7
8
9
10
// Category 是表示一个指向分类的结构体的指针,其定义如下:
typedef struct objc_category *Category;
struct objc_category {
  char *category_name                          OBJC2_UNAVAILABLE; // 分类名
  char *class_name                             OBJC2_UNAVAILABLE; // 分类所属的类名
  struct objc_method_list *instance_methods    OBJC2_UNAVAILABLE; // 实例方法列表
  struct objc_method_list *class_methods       OBJC2_UNAVAILABLE; // 类方法列表
  struct objc_protocol_list *protocols         OBJC2_UNAVAILABLE; // 分类所实现的协议列表
}

iOS-分类(Category)