默认情况下,分类不能添加成员变量,我们可以通过 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc 分类名XXX.m
转为c++查看一下分类的底层的本质结构组成,如下所示:
在终端通过 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person+Test.m
命令生成Person+Test.cpp文件,查看Person+Test.cpp文件
这个结构体的分类底层结构如下:
struct _category_t {
const char *name;
struct _class_t *cls;
const struct _method_list_t *instance_methods;
const struct _method_list_t *class_methods;
const struct _protocol_list_t *protocols;
const struct _prop_list_t *properties;
};
分类里面存在的内容示意:
① name 一般指的是类名
② cls 类
③ _method_list_t *instance_methods 方法列表-对象方法
④ struct _method_list_t *class_methods 方法列表-类方法
⑤ _protocol_list_t *protocols 协议
⑥ _prop_list_t *properties 属性
所以,分类里面也可以遵守协议,可以声明属性,可以写对象方法,类方法等。
此时,当我们在一个分类里面声明一个age属性的时候并不会报错,但是外部Person类调用这个属性赋值的时候,并不会真正赋值,这是因为分类里面并没有自动帮age属性实现- (void)setAge:(NSInteger)age 和- (NSInteger)age方法以及_age的成员变量,未解决这个问题,苹果给我们提供了关联属性的方案。
- 添加管理对象
/**
1.id _Nonnull object 关联的对象
2.const void * _Nonnull key 传入指针
3.id _Nullable value 管理的值
4.objc_AssociationPolicy policy 管理策略
4.1 OBJC_ASSOCIATION_ASSIGN = 0,
4.2 OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
4.3 OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
4.4 OBJC_ASSOCIATION_RETAIN = 01401,
4.5 OBJC_ASSOCIATION_COPY = 01403
*/
objc_setAssociatedObject(id _Nonnull NSObject, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy);
objc_AssociationPolicy | 对应的修饰符 |
---|---|
OBJC_ASSOCIATION_ASSIGN = 0, | assgin |
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, | strong , nonatomic |
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, | copy , nonatomic |
OBJC_ASSOCIATION_RETAIN = 01401, | strong,atomic |
OBJC_ASSOCIATION_COPY = 01403 | copy,atomic |
- 获取管理对象
/**
1.id _Nonnull object 关联的对象
2.const void * _Nonnull key 传入的key
*/
objc_getAssociatedObject(<#id _Nonnull object#>, <#const void * _Nonnull key#>);
- 移除所有的关联对象
/**
1.id _Nonnull object 管理对象
*/
objc_removeAssociatedObjects(<#id _Nonnull object#>)
项目中使用示例如下:
【用法1】
static const void *NameKey = &NameKey;
static const void *WeightKey = &WeightKey;
- (void)setName:(NSString *)name {
objc_setAssociatedObject(self, NameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
return objc_getAssociatedObject(self, NameKey);
}
- (void)setWeight:(int)weight {
objc_setAssociatedObject(self, WeightKey, @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (int)weight {
return [objc_getAssociatedObject(self, WeightKey) intValue];
}
【用法2】
static const char NameKey;
static const char WeightKey;
- (void)setName:(NSString *)name {
objc_setAssociatedObject(self, &NameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
return objc_getAssociatedObject(self, &NameKey);
}
- (void)setWeight:(int)weight {
objc_setAssociatedObject(self, &WeightKey, @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (int)weight {
return [objc_getAssociatedObject(self, &WeightKey) intValue];
}
【用法3】
#define MJNameKey @"name"
#define MJWeightKey @"weight"
- (void)setName:(NSString *)name {
objc_setAssociatedObject(self, MJNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
return objc_getAssociatedObject(self, MJNameKey);
}
- (void)setWeight:(int)weight {
objc_setAssociatedObject(self, MJWeightKey, @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (int)weight {
return [objc_getAssociatedObject(self, MJWeightKey) intValue];
}
【用法4】
///用法3
- (void)setName:(NSString *)name {
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
// 隐式参数
// _cmd == @selector(name)
return objc_getAssociatedObject(self, _cmd);
}
- (void)setWeight:(int)weight {
objc_setAssociatedObject(self, @selector(weight), @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (int)weight {
// _cmd == @selector(weight)
return [objc_getAssociatedObject(self, _cmd) intValue];
}
开发中本人多用前两种写法居多。后面两种仅供学习了解。方法1 vs 方法2 ,void *NameKey
是个指针变量,需要占用8个字节, char NameKey
只占一个字节,更节省内存空间。