分类可以随时给类添加方法,这种添加不需要去创建子类的,而且添加的方法可以被子类继承, 使用category可以很好的将类中的方法进行分类管理。但是分类最大的限制就是不能添加属性, 下面就分类不能添加属性原理以及替代方式做一下分析.
分类的内存结构如下:
//Category表示一个结构体指针的类型
typedef struct objc_category *Category;
struct objc_category {
char * _Nonnull category_name OBJC2_UNAVAILABLE;
char * _Nonnull class_name OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable instance_methods OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable class_methods OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
} OBJC2_UNAVAILABLE;
类的内存结构如下:
//Class也表示一个结构体指针的类型
typedef struct objc_class *Class;
struct objc_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;
对比可以发现category中少了 struct objc_ivar_list * _Nullable ivars
也就是说没有ivar
数组,所以无法添加属性。
分类并不会改变原有类的内存分布的情况,它是在运行期间决定的,此时内存的分布已经确定,若此时再添加实例会改变内存的分布情况,这对编译性语言是灾难,是不允许的。
通过关联对象给分类添加属性(其实是get和set方法)
@interface Dog (ext)
@property (nonatomic, strong)UIColor *color;
@end
@implementation Dog (ext)
//这里用@selector(color)来用作 const void *key 的指针
const void *key = "color";
- (UIColor *)color {
return objc_getAssociatedObject(self, key);
}
- (void)setColor:(UIColor *)color {
objc_setAssociatedObject(self, @selector(key), key, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
将一个选定值(value)通过Key-Value的形式挂载在目标对象(object)上,同时指定关联的策略(policy),这样就能生成一个关联对象。
通过将目标对象(object)上指定的Key对应的值(value)设置nil,即可以将已存在的关联对象清除。
并且使用关联属性还有如下好处:
- 可以看出Runtime对于关联对象的管理都是线程安全的,增删查改都是加锁的。
- 在App运行期间,由AssociationsHashMap来管理所有被添加到对象中的关联对象。
- NSObject在dealloc的时候不会清理关联对象,需要手动维护关联对象的内存管理。