Category相关的那些问题

2,345 阅读5分钟

1、 Objective-C的类可以多重继承么?可以实现多个接口么?

Objective-c的类不可以有多继承,OC里面都是单继承,多继承可以用protocol委托代理来模拟实现,可以通过实现多个接口 完成OC的多重继承 


2、 Category是什么?重写一个类的方式用继承好还是分类好?

Category是类别;  

⚠️重写一个类的方式用继承好还是分类好: 重写一个类的方式用继承还是分类.取决于具体情况.
假如目标类有许多的子类,我们需要拓展这个类又不希望影响到原有的代码,继承比较好。
如果仅仅是拓展方法,分类更好(不需要涉及到原先的代码)
分类中方法的优先级比原来类中的方法高,也就是说,在分类中重写了原来类中的方法,那么分类中的方法会覆盖原来类中的方法。


3、⚠️为什么不要在category中重写一个类原有的方法:

1、category没有办法去代替子类,它不能像子类一样通过super去调用父类的方法实现。
如果category中重写覆盖了当前类中的某个方法,那么这个当前类中的原始方法实现,将永远不会被执行,这在某些方法里是致命的。
(ps:这里提一下,+(void)load方法是一个特例,它会在当前类执行完之后再在category中执行。)
 2、同时,一个category不能可靠覆盖另一个category中相同的类的相同的方法。
(编译顺序决定)
 3、通过观察头文件我们可以发现,Cocoa框架中的许多类都是通过category来实现功能的,可能不经意间你就覆盖了这些方法中的其一,有时候就会产生一些无法排查的异常原因
4、category的诞生只是为了让开发者更加方便的去拓展一个类,它的初衷并不是让你去改变一个类。
结论: 要重写方法,当然我们首推通过子类重写父类的方法,在一些不方便重写的情况下,我们也可以在category中用runtime进行method swizzling(方法的偷梁换柱)来实现。


4、Category的好处?

  • 可以减少单个文件的体积

  • 可以把不同的功能组织到不同的category里

  • 可以由多个开发者共同完成一个类

  • 可以按需加载想要的category

  • 声明私有方法

5、category特点

  • category只能给某个已有的类扩充方法,不能扩充成员变量

  • category中也可以添加属性,只不过@property只会生成setter和getter的声明,不会生成setter和getter的实现以及成员变量。

  • 如果category中的方法和类中原有方法同名,运行时会优先调用category中的方法。也就是,category中的方法会覆盖掉类中原有的方法。所以开发中尽量保证不要让分类中的方法和原有类中的方法名相同。避免出现这种情况的解决方案是给分类的方法名统一添加前缀。比如category_。

  • 如果多个category中存在同名的方法,运行时到底调用哪个方法由编译器决定,最后一个参与编译的方法会被调用。


6. 方法调用优先级?

分类(category) > 本类 > 父类。
(load方法特殊,在父类方法执行后在子类和分类中仍会执行)

7. 为什么category不能添加 成员变量 ?

这要看 Objective-C类 是由Class类型的isa来表示的,
@interface NSObject <NSObject> {
Class isa 
}

Class实际上是一个指向objc_class结构体的指针。
objc_class结构体的定义如下:
{
Class isa
Class super_class  // 父类 
const char *name //类名
long version          //类版本信息
long info               //类信息
long instance_size                  //类实例变量大小
struct objc_ivar_list *ivars               // 类的成员变量、链表
struct objc_method_list **methodLists //方法定义的链表、二维数组
struct objc_cache *cache                   //方法缓存
struct objc_protocol_list *protocols  //协议链表
}

在Runtime中,objc_class结构体大小是固定的,不可能往这个结构体中添加数据,只能修改

所以ivars指向的是一个固定区域,只能修改成员变量值,不能增加成员变量个数。

methodList是一个二维数组,所以可以修改 *methodLists的值 来增加成员方法虽没办法扩展methodLists指向的内存区域,却可以改变这个内存区域的值(存储的是指针)
因此,可以动态添加方法,不能添加成员变量。


8、category中能添加属性吗?

这个我们要从Category的结构体开始分析:

typedef struct category_t {
const char *name; //类的名字
classref_t cls; //
struct method_list_t *instanceMethods; //category中所有给类添加的  实例方法的列表
struct method_list_t *classMethods; //category中所有添加的  类方法的列表
struct protocol_list_t *protocols; //category实现的所有  协议的列表
struct property_list_t *instanceProperties; //category中添加的 所有属性
} category_t;

也可以看出Category的可为(可以添加实例方法,类方法,甚至可以实现协议,添加属性
和不可为(无法添加实例变量

Category实际上允许添加属性的,同样可以使用@property,但是不会生成_变量(带下划线的成员变量),也不会生成添加属性的getter和setter方法的实现,所以,尽管添加了属性,也无法使用点语法调用getter和setter方法
(实际上,点语法是可以写的,只不过在运行时调用到这个方法时候会报方法找不到的错误,如下图)。
但使用中可以使用runtime去实现Category为已有的类添加新的属性并生成getter和setter方法。



extension被开发者称之为扩展、延展、匿名分类。extension一般用于声明私有方法,私有属性,私有成员变量。

extension  可以声明  方法、声明属性、成员变量。