Category是运行时决议生效的,Extension是编译时就决议生效的
Category不可以添加实例变量,Extension可以。
Extension和category都可以添加属性,但是Category的属性不能生成成员变量和getter、setter方法的实现
Category是有声明和实现,Extension是直接写在宿主类的.m文件中,只有声明
Category
先看看Category
的结构体
struct category_t {
const char *name; ///类名
classref_t cls; ///类指针
struct method_list_t *instanceMethods; ///实例方法列表
struct method_list_t *classMethods; ///类方法列表
struct protocol_list_t *protocols; ///遵守的协议
struct property_list_t *instanceProperties; /// 实例属性列表
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties; /// 类属性列表
method_list_t *methodsForMeta(bool isMeta) { ///获取实例方法列表或类方法列表
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi); ///获取实例属性列表和类属性列表。
};
从category结构体可以看出来,category可以添加实例方法,类方法,甚至可以实现协议,添加属性
小贴士:类属性类和实例都可以访问,实例属性类不可以访问只有类能访问
所以category是允许添加属性的,同样可以使用@property,但是不能生成成员变量,也不能生成添加属性的getter和setter方法的实现,所以尽管添加了属性,也无法使用点语法调用getter和setter方法。但是可以使用runtime去实现category为已有的类添加新的属性并生产getter和setter方法。
那为什么Category的属性不能为类添加成员变量,原因要看runtime中class的结构体.
如下,类结构体中的 objc_ivar_list 实例变量的链表和 instance_size 实例变量的内存大小在编译时已经确定,而Category又是运行时,因此无法向已经编译完成的类中增加实例变量。
typedef struct objc_class *Class;
struct objc_class {
//结构体的第一个成员变量也是isa指针,这就说明了Class本身其实也是一个对象,因此我们称之为类对象,类对象在编译期产生用于创建实例对象,是单例。
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
Extension
那为什么Extension可以添加呢。其实上面已经说明了答案,Extension是编译时,因此可以在类的结构体编译完成前将成员变量加上
网友评论