源代码copy
不废话,直接附上实现的源代码:
相关API声明在objc-runtime.mm中
1. 设置相关对象的值
/** 为类的实例对象设置相关对象 */
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结构实例
ObjcAssociation old_association(0, nil);
// 对传入的新值value根据policy进行内存处理,并返回对象地址
id new_value = value ? acquireValue(value, policy) : nil;
{
AssociationsManager manager;
// 获取全局相关对象hashmap的起始地址
AssociationsHashMap &associations(manager.associations());
// object地址转换为查询地址
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
// 查询全局哈希表中是否存在object的相关对象信息的容器
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// secondary table exists
// 存在object的相关对象信息容器(也是一张子哈希表)
// 获取字表的起始地址
ObjectAssociationMap *refs = i->second;
// 查找是否存在key(即新加的属性)的相关信息
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
// 存在相同的相关信息(以前存储过了),直接修改
// 将以前的值缓存给old_assosiation
old_association = j->second;
// 在原地址中存入新值(ObjcAssociation结构体实例,成员为policy和new_value)
j->second = ObjcAssociation(policy, new_value);
} else {
// 在该object中没有此属性key的相关信息保存,创建新的,直接存储
// 在该地址下,存入键值对:键名为属性名key,值为ObjcAssociation结构体实例
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
// create the new association (first time).
// 全局哈希表中没有存储object的相关对象容器,创建新的容器(ObjectAssociationMap对象,也是个哈希表)
ObjectAssociationMap *refs = new ObjectAssociationMap;
// 将新创建的容器指针存储到全局哈希表中,键名为object的地址
associations[disguised_object] = refs;
// 在容器中存入相关对象值(ObjcAssociation结构体实例),键名为属性名key
(*refs)[key] = ObjcAssociation(policy, new_value);
// 标记object为存在相关对象信息(修改isa联合体实例中的相关位的值)
object->setHasAssociatedObjects();
}
} else {
// setting the association to nil breaks the association.
// 传入的相关值是nil,则清除该相关对象的信息(清除属性的值)
// 使用object地址再全局哈希表中查找,试图找到object的相关对象容器
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// 已找到此容器(子哈希表,ObjectAssociationMap对象)
ObjectAssociationMap *refs = i->second;
// 查找此属性名key存储的信息
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
// 查找到相关对象信息,缓存给old_association
old_association = j->second;
// 清除此ObjectAssociationMap对象(相关对象实例)
refs->erase(j);
}
}
}
}
// release the old value (outside of the lock).
// 缓存的old_association中存在值,释放内存,清除缓存
if (old_association.hasValue()) ReleaseValue()(old_association);
}
从代码中可以看出,所有的相关对象信息实际上是保存在全局的一个哈希表中。其中,以实例对象的地址( DISGUISE(object) )为键名,对应的值为另一张哈希表。此子哈希表中,以设置的属性名为键名,对应的值为相关值value和内存管理方式policy组成的结构体实例。
而保存相关对象的值的操作中,系统:
- 首先查看是否已经存储过此object对应的子哈希表,不存在则创建;
- 查看子哈希表中,是否已经存储过此属性名key对应的结构体信息,存在即修改为新值,不存在则创建新的保存;
- 对与传入的value为nil时,清除子哈希表中属性名key对应的结构体信息,以达到清除相关值的目的(系统没有提供单独清空相关值属性的API )。
- 替换下来的相关值信息,抹掉并释放内存。
2. 获取相关对象的值
/** 为类的实例对象获取相关对象的值 */
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;
// 声明内存管理方式指针变量(默认为assign)
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
AssociationsManager manager;
// 获取全局相关对象哈希表(起始地址)【由于associations得到的是指向AssociationsHashMap的指针,故使用取地址符返回AssociationsHashMap的起始地址】
AssociationsHashMap &associations(manager.associations());
// 将object地址转换为查询地址(保存地址)
disguised_ptr_t disguised_object = DISGUISE(object);
// 视图查找到object对应的相关对象信息容器(AssociationsHashMap对象)
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// 已找到,获取入口地址(ObjectAssociationMap对象地址)
ObjectAssociationMap *refs = i->second;
// 查找ObjectAssociationMap中,是否存在属性名key对应的相关信息实例
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
// 已找到,记录入口地址(ObjcAssociation结构体实例的指针)
ObjcAssociation &entry = j->second;
// 取出对应的值和内存管理方式
value = entry.value();
policy = entry.policy();
if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
// 通过按位与运算,对需要retain的值,进行retain操作
objc_retain(value);
}
}
}
}
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
// 存在相关值,且内存管理方式为autorelease,进行autorelease操作
objc_autorelease(value);
}
// 返回相关值
return value;
}
3. 为实例对象清除所有的相关对象
/** 清除类的实例对象的所有相关对象 */
void objc_removeAssociatedObjects(id object)
{
// 查看对象isa联合体中has_assoc位的值来确定是否存在相关对象
if (object && object->hasAssociatedObjects()) {
_object_remove_assocations(object);
}
}
// 实现
void _object_remove_assocations(id object) {
vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
{
// 获取全局相关对象的整体哈希表
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
// 本身没有存储任何信息,直接返回
if (associations.size() == 0) return;
// 将object指针转换为查询的指针
disguised_ptr_t disguised_object = DISGUISE(object);
// 试图查询是否存在object的相关对象容器信息
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// copy all of the associations that need to be removed.
// 已查到,获取入口地址
ObjectAssociationMap *refs = i->second;
// 依次遍历子哈希表(ObjectAssociationMap对象的键值对信息)
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
// 头插法插入到向量对象中
elements.push_back(j->second);
}
// remove the secondary table.
// 删除入口地址信息
delete refs;
// 清除子表(object容器中存储的所有信息,即ObjectAssociationMap对象)
associations.erase(i);
// 清除之后,object便没有任何相关对象保存
}
}
// the calls to releaseValue() happen outside of the lock.
// 对向量中存储的所有相关对象信息,依次释放内存
for_each(elements.begin(), elements.end(), ReleaseValue());
}
相关问题
1. 在Category中添加的property属性,系统为什么不会自动合成setter和getter?
- 在编译期,类的内存布局已经确定,在Class的结构中,并没有专门存储Category相关信息的位置。
- Category是在APP启动时,在运行期才进行加载的,且是在对应的类加载完毕后才进行。最重要的,加载Category时,系统只是将内部声明的方法附加到Class的对应方法列表中,并不会对类的ivar列表进行操作。
- 最重要的,objc_category_t结构体中,根本没有变量成员,故自身也不能存储任何变量的值。
2. 相关对象是针对类的还是实例对象的?
显而易见,由API的实现,可以看到,所有的相关对象是与object参数,即类的实例对象进行绑定关联,并存储到全局哈希表中的。故设置的相关对象只对当前的调用者对象有效。
网友评论