OC 分类(Category)添加属性

4,238 阅读2分钟

在 objc 源码中,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);
};

可以看到其中结构体指针 instanceProperties-属性列表,因此我们可以直接给分类添加属性 如:Person+Run 分类,添加属性 speed

#import "Person.h"

@interface Person (Run)

@property (nonatomic, assign) CGFloat speed;

@end

struct category_t 的结构体可以看到,其中没有像类的结构体一样的 ivars - 成员变量列表,因此我们不能直接添加成员。
比如我们尝试给 Person+Run 分类添加 _speed 成员变量,编译直接报错

我们知道在 OC 类中申明属性 xxx 时,会自动生成成员变量 _xxx,生成 set 和 get 方法的申明以及 set 和 get 方法的实现。

而在分类中添加属性时,则只会生成 set 和 get 方法的申明。因此我们在分类中添加了属性,如果直接调用 se t或者 get 也是会发生崩溃(set 和 get 只有申明,没有实现)。

所以:
1、OC 分类中可以添加属性,但是不会生成对应的成员变量以及 set 和 get 方法; 2、OC 分类中不能添加成员变量。

既然我们可以在分类中添加属性,那么有没有什么办法让在分类中属性能够像在类的属性那样使用,比如调用 set 和 get 方法取呢?

答案是可以的,通过方法很多,比如在分类的 .m 中添加全局变量(比如字典),然后在手动实现set、get方法。<br

当然最优解是:关联对象 话不多说,直接上代码:

Person+Run.h
@interface Person (Run)

@property (nonatomic, assign) CGFloat speed;

@end


Person+Run.m  
static const char NameKey;

@implementation Person (Run)

- (void)setName:(NSString *)name
{
    objc_setAssociatedObject(self, &NameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)name
{
    return objc_getAssociatedObject(self,  &NameKey);
}

@end

其中设置关联属性函数:

/// @param object 表示关联对象,即要为哪个对象关联对象
/// @param key name 获取被关联对象的索引key
/// @param value 被关联对象
/// @param policy 关联策略
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

外面可以按正常属性进行使用: