在 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)
外面可以按正常属性进行使用: