众所周知,在分类中可以定义属性,但是它不会自动生产settet/getter方法,也不会生成对应的实例变量。所以在分类中使用属性的话,需要自己去实现settet/getter方法。在setter和getter方法中,使用关联对象的方法来存储属性的值。
#import "LGPerson.h"
@interface LGPerson (LG) //LGPerson的分类。
@property (nonatomic, copy) NSString *cate_name;
@end
@implementation LGPerson (LG)
-(void)setCate_name:(NSString *)cate_name{ // setter方法
/**
参数一:id object : 给哪个对象添加属性,这里要给自己添加属性,用self。
参数二:void * == id key : 属性名,根据key获取关联对象的属性的值,在objc_getAssociatedObject中通过次key获得属性的值并返回。
参数三:id value : 关联的值,也就是set方法传入的值给属性去保存。
参数四:objc_AssociationPolicy policy : 策略,属性以什么形式保存。
*/
objc_setAssociatedObject(self, @"name",cate_name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSString *)cate_name{ // getter方法
/**
参数一:id object : 获取哪个对象里面的关联的属性。
参数二:void * == id key : 什么属性,与objc_setAssociatedObject中的key相对应,即通过key值取出value。
*/
return objc_getAssociatedObject(self, @"name");
}
那么关联对象objc_setAssociatedObject 和 objc_getAssociatedObject底层是怎么实现的呢? show you code:
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);
}
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// retain the new value (if any) outside the lock.
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// secondary table exists
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);
} else {
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
// create the new association (first time).
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
}
} else {
// setting the association to nil breaks the association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
refs->erase(j);
}
}
}
}
// release the old value (outside of the lock).
if (old_association.hasValue()) ReleaseValue()(old_association);
}
id objc_getAssociatedObject(id object, const void *key) {
return _object_get_associative_reference(object, (void *)key);
}
id _object_get_associative_reference(id object, void *key) {
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
ObjcAssociation &entry = j->second;
value = entry.value();
policy = entry.policy();
if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
objc_retain(value);
}
}
}
}
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
objc_autorelease(value);
}
return value;
}
通过底层的源码我们可以看出,关联对象是通过AssociationsManager来管理
AssociationsHashMap,从而来存储属性的值的。
当我们通过关联对象来存值的时候,其流程为:
- 通过AssociationsManager获取到全局的AssociationsHashMap
- 首先判断属性的value是否存在,若为nil,那么则表示要把之前存的值清空。(这里会有一个查找的流程,找到旧值,把它干掉,如果没有旧值,则啥都不干。)
- 通过对象做为key去find当前self所管理的属性的ObjectAssociationMap。
- 3.1. 如果能找到当前对象的ObjectAssociationMap,则在ObjectAssociationMap中通过属性的name做为key,去找ObjectAssociation对象,如果找到了,就修改之前的ObjectAssociation对象中的value和policy的值。如果没有找到,则说明第一次存,就新建一个ObjectAssociation对象,并把它关联到ObjectAssociationMap中去。
- 3.2. 如果未能找到当前对象的ObjectAssociationMap,则新建一个ObjectAssociationMap对象和ObjectAssociation对象,把value和policy的值存进去,并把ObjectAssociationMap关联到AssociationsHashMap中去。
当我们通过关联对象来取值的时候,其过程和存值的情况类似,也是一个查找的流程,这里具体就不赘述了。
图片来自网络上的大神总结的,侵权必删。
最后,关联对象的ObjectAssociationMap,以及ObjectAssociation是什么时候被remove的呢,答案就是当对象被delloc的时候,delloc的时候,会去查找当前对象是否有关联的属性,有的话,就会把对应的内容都删除掉。
网友评论