O、题:Category能否添加成员变量?如果可以,如何给Category添加成员变量?
答:不能直接给Category添加成员变量(由分类的结构决定的),但是可以间接实现Category有成员变量的效果。从分类的底层实现
category_t
结构体也可以看出来,分类是不能直接添加成员变量的,因为category_t
结构体中没有地方存放成员变量。
给Category间接添加成员变量方式:使用关联对象。
拓展:
A. 如果在类
里面写一个属性,系统会帮我们自动在类里面添加该属性的成员变量,set,get方法实现。
@property (nonatomic, assign) int age;
等价于:
@interface LQPerson : NSObject
{
int _age;
}
//@property (nonatomic, assign) int age;
@end
@implementation LQPerson
- (void)setAge:(int)age {
_age = age;
}
- (int)age {
return _age;
}
@end
B. 如果在分类
里面写一个属性,系统不会帮我们自动在类里面添加该属性的成员变量,set,get方法实现。只会添加set,get方法申明。
@property (nonatomic, assign) int age;
等价于
@interface LQPerson : NSObject
//@property (nonatomic, assign) int age;
- (void)setAge:(int)age;
- (int)age;
@end
问题:想要在分类中的属性跟类中的属性使用效果相同,该怎么办?
答:
A:在m文件中声明一个全局变量,在set方法中保存属性的值,在get方法中返回保存的属性的值(即全局变量的值)。
@implementation LQPerson (Test)
int age_;
- (void)setAge:(int)age {
age_ = age;
}
- (int)age {
return age_;
}
@end
该方法存在的问题:
这个属性变成全局性的了,如果该类有多个实例对象,就会出现所有的这个类的实例对象的该属性的值都相同。
B:由于A方法中的值不能一一对应,推倒出B方法,在m文件中声明一个全局动态字典,在set方法中,用
self
作为key,值作为value
来保存属性的值。在get方法中返回用self
作为key
,在全局字典中取出value
的值。
@implementation LQPerson (Test)
NSMutableDictionary *weights_;
+ (void)load {
weights_ = [NSMutableDictionary dictionary];
}
- (void)setWeight:(int)weight {
NSString *key = [NSString stringWithFormat:@"%p", self];
weights_[key] = @(weight);
}
- (int)weight {
NSString *key = [NSString stringWithFormat:@"%p", self];
return [weights_[key] intValue];
}
@end
---成功---:
该方式与类中的属性的区别是:类里面的属性的值是存储在该类的实例对象内部,而分类中间接实现的属性的值存储在全局的字典对象里面。
---问题---:
线程安全问题(可以适应锁解决),比较麻烦。
C:使用关联对象
关联对象API:
// 添加关联对象
void objc_setAssociatedObject(id object, const void * key,
id value, objc_AssociationPolicy policy)
// 获得关联对象
id objc_getAssociatedObject(id object, const void * key)
// 移除单个属性的关联对象(把该属性的值设置为nil即可)
void objc_setAssociatedObject(id object, const void * key,
nil, objc_AssociationPolicy policy)
// 移除所有的关联对象
void objc_removeAssociatedObjects(id object)
关联策略:
关联策略#import <objc/runtime.h>
@implementation LQPerson (Test)
const void* LQNameKey; // LQNameKey = NULL
- (void)setName:(NSString *)name {
objc_setAssociatedObject(self, LQName, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
return objc_getAssociatedObject(self, LQName);
}
const void* LQAgeKey;
- (void)setAge:(int)age {
// @(age)是NSNumber类型,使用OBJC_ASSOCIATION_RETAIN_NONATOMIC
objc_setAssociatedObject(self, LQAgeKey, @(age), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (int)age {
return [objc_getAssociatedObject(self, LQNameKey) intValue];
}
@end
存在的问题:
LQNameKey、LQAgeKey没有赋值,就相当于NULL
,如果有多个属性,关联的值就会出现混乱。后面赋值的属性会把前面的属性值覆盖。
解决方法:
static const void* LQNameKey = &LQNameKey;
static const void* LQAgeKey = &LQAgeKey;
处理关联对象的key
// 方案一:使用指针地址作为key
static const char kNamekey;
static const char kAgeKey;
objc_setAssociatedObject(self, &kNamekey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_getAssociatedObject(self, &kNamekey);
objc_setAssociatedObject(self, &kAgeKey, @(age), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_getAssociatedObject(self, &kAgeKey)
// 方案二:使用属性名作为key,直接放字符串,相当于使用的是该字符串的地址值
pobjc_setAssociatedObject(obj, @"property", value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
pobjc_getAssociatedObject(obj, @"property");
// 方案三:使用get方法的@selecor作为key,在get方法中可以使用_cmd
// _cmd相当于获取当前方法的@selector
pobjc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
pobjc_getAssociatedObject(obj, @selector(getter))
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_getAssociatedObject(self, @selector(name));
objc_setAssociatedObject(self, @selector(age), @(age), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_getAssociatedObject(self, @selector(age))
网友评论