分类添加属性
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);
protocol_list_t *protocolsForMeta(bool isMeta) {
if (isMeta) return nullptr;
else return protocols;
}
};
Category能否添加成员变量?如果可以,如何给Category添加成员变量?
不能直接给Category添加成员变量,但是可以使用关联对象,间接实现Category有成员变量的效果
如果我们给类添加一个属性,如下代码
@property (nonatomic, assign) int age;
相当于做了三件事,1,创建_age成员变量;2.生成设方法和get方法的声明;3.生成这两个方法的具体实现。
如果我们在分类中添加属性,1他只会做生成方法的声明
我们可以思考 我们是不是可以自己去实现,我们自己去写分类的set和get方法的实现,发现他编译会报错Instance variables may not be placed in categories
如果我们在分类增加属性,其实也就是想以后想普通的成员变量一样使用,p.age 方式来取值和设置值
我们之前可以用.age去值是因为,我们设置的时候,将10赋给了_age这个成员变量,
02使用字典完善数组
可以在set方法的时候用一个全局变量,将值存放起来,但是全局变量有个缺陷,如果我们创建多个person对象,他们就是共用这个变量,导致数据混乱。
可以使用一个字典,来实现每一个peroson对象,对应自己的那个变量
NSMutableDictionary *weight_;
+ (void)load
{
weight_ = [NSMutableDictionary dictionary];
}
- (void)setAge:(int)age
{
NSString *key = [NSString stringWithFormat:@"%p", self];
weight_[key] = @(age);
}
- (int)age
{
NSString *key = [NSString stringWithFormat:@"%p", self];
return [weight_[key] intValue];
}
3使用字典存在的问题
成员变量的10是在person对象的内存结构中的,我们设置的age他会存在于全局字典对象里面,他会一值在内存中,存在线程安全问题,假如不同线程里面的person对象都要访问这个属性,就会造成多个线程共同访问一个对象
如若我们想增加一个属性,还要在增加一个字典对象。开发工程中我们一般不采用这种方式,
04关联对象的基本使用
利用关联对象来实现给分类添加属性,利用runtime的函数来实现
关联对象提供了以下API
添加关联对象
/**
object:给哪个对象关联
key:关联的key
value:关联的对象
policy:内存策略
*/
void objc_setAssociatedObject(id object, const void * key,
id value, objc_AssociationPolicy policy)
获得关联对象
id objc_getAssociatedObject(id object, const void * key)
移除所有的关联对象
void objc_removeAssociatedObjects(id object)
关联策略
const void *Namekey = &Namekey;
- (void)setName:(NSString *)name
{
objc_setAssociatedObject(self, Namekey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name
{
return objc_getAssociatedObject(self, Namekey);
}
key的用法
static void *MyKey = &MyKey;
objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, MyKey)
static char MyKey;// 因为我们主要用他的地址,所以我们用个内存比较小的类型。
objc_setAssociatedObject(obj, &MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, &MyKey)
使用属性名作为key//这种直接写出来字符串,他是存在常量区的,无论写多少遍他都是同一个对象。
objc_setAssociatedObject(obj, @"property", value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_getAssociatedObject(obj, @"property");
使用get方法的@selecor作为key//简单编译器提示,
objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, @selector(getter))
用全局变量做key的话存在问题,别人可以拿到并可以改里面的值,我们本意是不希望别人访问这个key,可以在前面加上static像第一种做饭
extern const void *NameKey;
06答疑
给类对象关联属性,把id给成类对象关联就可以,
分类的结构决定了 它不能添加成员变量,他没有存放成员变量的属性,
分类不能直接添加成员变量的,原来的类有个ivars来存放成员变量的,关联对象不是把这个对象当成成员变量,放到这个ivars数组里面的,他不会影响来的类对象和实例对象的结构
07底层的数据结构
实现关联对象技术的核心对象有
AssociationsManager->
AssociationsHashMap //字典->
ObjectAssociationMap->
ObjcAssociation->策略和值
objc_setAssociatedObject->_object_set_associative_reference
08底层数据结构
void objc_setAssociatedObject(id object1, const void * key,
id value,
objc_AssociationPolicy policy)
关联对象并不是存储在被关联对象本身内存中
关联对象存储在全局的统一的一个AssociationsManager中
设置关联对象为nil,就相当于是移除关联对象
内存策略里并没有弱引用,
对象释放这个关联对象是不是被移除
肯定被移除