分类无法添加属性分析以及其替代方式

分类可以随时给类添加方法,这种添加不需要去创建子类的,而且添加的方法可以被子类继承, 使用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的时候不会清理关联对象,需要手动维护关联对象的内存管理。