博客连接 从源码理解关联属性数组
在类中,咱们使用@property (nonatomic, copy) NSString *name
生成一个属性。它干了三件事情:安全
_name
的变量;setName:
和getName
方法;可是在分类中写上述这样一个属性的,它只有setter和getter方法的声明,并不会生成成员变量和实现setter和getter方法,所以若是想要在分类中实现属性的话得使用关联对象的方式。bash
首先咱们要明白为何要使用关联对象? 在分类中@property
并不会自动生成实例变量以及存取方法,另外在分类是不能声明成员变量的。从源码的角度去看,Category在编译时期生成的结构体中根本没有存放成员变量的数组。基于上面的缘由,若是咱们要实现类中属性那样的效果,就要使用关联对象。数据结构
关联对象的应用以下:app
// .h
@interface FatherA : NSObject
@property (nonatomic, copy) NSString *name;
@end
// .m
@implementation FatherA
- (void)setName:(NSString *)name {
objc_setAssociatedObject(self,
@selector(name),
name,
OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
return objc_getAssociatedObject(self, _cmd);
}
@end
复制代码
这里使用objc4-750.1
的源代码,你能够经过这里下载。咱们经常使用的关于关联对象的API主要有如下几个:ide
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);
复制代码
这三个方法的做用分别是:函数
在分析关联对象的API实现以前,先看一下关联对象的核心对象。ui
AssociationsManager
的定义以下:atom
spinlock_t AssociationsManagerLock;
class AssociationsManager {
// associative references: object pointer -> PtrPtrHashMap.
static AssociationsHashMap *_map;
public:
AssociationsManager() { AssociationsManagerLock.lock(); }
~AssociationsManager() { AssociationsManagerLock.unlock(); }
AssociationsHashMap &associations() {
if (_map == NULL)
_map = new AssociationsHashMap();
return *_map;
}
};
AssociationsHashMap *AssociationsManager::_map = NULL;
复制代码
AssociationsManager
会维护一个AssociationsHashMap
,在初始化的时候,调用AssociationsManagerLock.lock()
,在析构时会调用AssociationsManagerLock.unlock()
,而associations
用于取得一个全局的AssociationsHashMap
。spa
另外AssociationsManager
经过一个自旋锁spinlock_t AssociationsManagerLock
来确保对AssociationsHashMap
的操做是线程安全的。
HashMap
至关于OC中的NSDictionary
。AssociationsHashMap
的定义以下:
class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {
public:
void *operator new(size_t n) { return ::malloc(n); }
void operator delete(void *ptr) { ::free(ptr); }
};
复制代码
AssociationsHashMap
继承自unordered_map
,使用的是C++语法。它的做用就是保存从对象的disguised_ptr_t
到ObjectAssociationMap
的映射。 咱们能够理解为AssociationsHashMap以key-value的形式存着若干个ObjectAssociationMap。
ObjectAssociationMap
的定义以下:
class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {
public:
void *operator new(size_t n) { return ::malloc(n); }
void operator delete(void *ptr) { ::free(ptr); }
};
复制代码
ObjectAssociationMap
保存了从key
到ObjcAssociation
的映射,咱们能够理解为ObjectAssociationMap以key-value的形式存着若干个ObjcAssociation对象。这个数据结构保存了当前对象对应的全部关联对象:
ObjcAssociation
的定义以下:
class ObjcAssociation {
uintptr_t _policy;
id _value;
public:
ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
ObjcAssociation() : _policy(0), _value(nil) {}
uintptr_t policy() const { return _policy; }
id value() const { return _value; }
bool hasValue() { return _value != nil; }
};
复制代码
ObjcAssociation
对象保存了对象对应的关联对象,其中的_policy
和_value
字段存的即是咱们使用objc_setAssociatedObject
方法时传入的policy
和value
。
经过objc_setAssociatedObject
函数,咱们添加一个关联对象,其实现以下:
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);
}
复制代码
接着看一下_object_set_associative_reference
的实现:
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);
// 调用acquireValue对new_value进行retain或者copy
id new_value = value ? acquireValue(value, policy) : nil;
{
// 初始化一个AssociationsManager,并获取AssociationsHashMap
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
// disguised_ptr_t是AssociationsHashMap中的key,经过传入的object获得
disguised_ptr_t disguised_object = DISGUISE(object);
// new_value有值表明设置或者更新关联对象的值,不然表示删除一个关联对象
if (new_value) {
// break any existing association.
// 查找ObjectAssociationMap
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// secondary table exists
// ObjectAssociationMap存在
// 判断key是否存在,key存在更新原有的关联对象,key不存在,则新增,而且新增的位置须要结合end()
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不存在
// 初始化一个ObjectAssociationMap,再实例化ObjcAssociation对象添加到Map中,并调用 setHasAssociatedObjects函数
// setHasAssociatedObjects标明当前类具备关联类
// 它会将isa结构体中的has_assoc标记为true
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.
// 查找ObjectAssociationMap
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
// ObjectAssociationMap存在
// 判断key是否存在,key存在则调用erase函数来删除ObjectAssociationMap中key对应的节点
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).
// 原来的关联对象有值,调用ReleaseValue函数释放关联对象的值
if (old_association.hasValue()) ReleaseValue()(old_association);
}
复制代码
经过上面的源码以及注释能够知道objc_setAssociatedObject
的流程,接着咱们用一张图片来讲明关联对象的原理:
在理解了objc_setAssociatedObject
的实现以后,objc_getAssociatedObject
就变得容易理解了,其实现以下:
id objc_getAssociatedObject(id object, const void *key) {
return _object_get_associative_reference(object, (void *)key);
}
复制代码
接着看一下_object_get_associative_reference
的实现:
id _object_get_associative_reference(id object, void *key) {
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
// 初始化一个AssociationsManager,并获取AssociationsHashMap
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
// 经过object得到disguised_ptr_t,用做在AssociationsHashMap的key
disguised_ptr_t disguised_object = DISGUISE(object);
// 查找ObjectAssociationMap的位置
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
// 查找ObjcAssociation对象
if (j != refs->end()) {
ObjcAssociation &entry = j->second;
value = entry.value();
policy = entry.policy();
// 说明是强类型,retain操做
if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
objc_retain(value);
}
}
}
}
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
// autorelease
objc_autorelease(value);
}
return value;
}
复制代码
objc_removeAssociatedObjects
用来删除全部的关联对象,其实现:
void objc_removeAssociatedObjects(id object)
{
if (object && object->hasAssociatedObjects()) {
_object_remove_assocations(object);
}
}
复制代码
经过hasAssociatedObjects
函数判断是否含有关联对象,若是有则调用_object_remove_assocations
。
在objc_setAssociatedObject
的实现中有提到在添加关联对象的时候若是ObjectAssociationMap
不存在,则会初始化一个ObjectAssociationMap
,再实例化ObjcAssociation
对象添加到Map中,并调用 setHasAssociatedObjects
函数。setHasAssociatedObjects
函数用来将isa结构体中的has_assoc
标记为true
,而hasAssociatedObjects
函数则用来获取该该标志位的结果。
接着看一下_object_remove_assocations
,其实现以下:
void _object_remove_assocations(id object) {
vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
if (associations.size() == 0) return;
disguised_ptr_t disguised_object = DISGUISE(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;
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
elements.push_back(j->second);
}
// remove the secondary table.
delete refs;
associations.erase(i);
}
}
// the calls to releaseValue() happen outside of the lock.
for_each(elements.begin(), elements.end(), ReleaseValue());
}
复制代码
_object_remove_assocations
会将对象包含的全部关联对象加入到一个vector
中,删除AssociationsHashMap
中对应的节点,而后对全部的 ObjcAssociation
对象调用 ReleaseValue()
,释放再也不被须要的值。
关联对象的实现
ObjcAssociation
对象;ObjectAssociationMap
以key
为健存储关联对象的数据结构(ObjcAssociation
对象);ObjectAssociationMap
,对象中的has_assoc
用来肯定是否含有关联对象,而对象与ObjectAssociationMap
之间的映射关系则存储在AssociationsHashMap
中;AssociationsHashMap
是全局惟一的,有AssociationsManager
管理。分类中可否实现属性
若是将属性当作是实例变量,那答案是不能,若是将属性当作是存取方法以及存储值的集合,那么分类是能够实现属性的,我的更倾向于前者。
weak类型的关联对象
关联对象里是没有weak类型的策略,而在开发过程当中,真的几乎没有说要用弱类型的关联对象,除非是为了用而用。我是这么理解的,既然叫作关联对象,那确定须要和自身生命周期有联系才谈得上关联,使用weak则表明对象和自身生命周期是没有联系,自身的释放不会影响关联对象。综上我认为weak类型的关联对象是没有意义的。
可是若是非要实现一个weak类型的关联对象也不是不能够,拿个中间对象包装一下便可。代码以下:
#pragma mark - Weak Associated Object
@interface _NNWeakAssociatedWrapper : NSObject
@property (nonatomic, weak) id associatedObject;
@end
@implementation _NNWeakAssociatedWrapper
@end
void nn_objc_setWeakAssociatedObject(id object, const void * key, id value) {
_NNWeakAssociatedWrapper *wrapper = objc_getAssociatedObject(object, key);
if (!wrapper) {
wrapper = [_NNWeakAssociatedWrapper new];
objc_setAssociatedObject(object, key, wrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
wrapper.associatedObject = value;
}
id nn_objc_getWeakAssociatedObject(id object, const void * key) {
id wrapper = objc_getAssociatedObject(object, key);
id objc = wrapper && [wrapper isKindOfClass:_NNWeakAssociatedWrapper.class] ?
[(_NNWeakAssociatedWrapper *)wrapper associatedObject] :
nil;
return objc;
}
复制代码