一、关联对象 和 分类category
通过分类的加载原理;我们知道:分类中无法添加成员变量;添加属性也无法生成setter和setter方法。如果想在分类中强行添加方法,使用runtime
的关联对象是现在常见的一种解决方式。
下面就是在分类中添加关联对象的方式
MCPerson+Test.h
:
#import "MCPerson.h"
@interface MCPerson (Test)
@property(nonatomic,copy) NSString *name;
@end
MCPerson+Test.m
:
#import "MCPerson+Test.h"
#import <objc/runtime.h>
@implementation MCPerson (Test)
- (void)setName:(NSString *)name
{
objc_setAssociatedObject(self,"name", name, OBJC_ASSOCIATION_COPY);
}
- (NSString*)name
{
return objc_getAssociatedObject(self, "name");
}
@end
二、关联对象实现原理
我们查看objc_setAssociatedObject
方法的实现
void
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
SetAssocHook.get()(object, key, value, policy);
}
看看SetAssocHook
static ChainedHookFunction<objc_hook_setAssociatedObject> SetAssocHook{_base_objc_setAssociatedObject};
进一步查看_base_objc_setAssociatedObject
_base_objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
_object_set_associative_reference(object, key, value, policy);
}
通过一步步的函数调用,我们最终查看到_object_set_associative_reference
void
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
// This code used to work when nil was passed for object and key. Some code
// probably relies on that to not crash. Check and handle it explicitly.
// rdar://problem/44094390
if (!object && !value) return;
if (object->getIsa()->forbidsAssociatedObjects())
_objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
DisguisedPtr<objc_object> disguised{(objc_object *)object};
ObjcAssociation association{policy, value};
// retain the new value (if any) outside the lock.
association.acquireValue();
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
if (value) {
auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
if (refs_result.second) {
/* it's the first association we make */
object->setHasAssociatedObjects();
}
/* establish or replace the association */
auto &refs = refs_result.first->second;
auto result = refs.try_emplace(key, std::move(association));
if (!result.second) {
association.swap(result.first->second);
}
} else {
auto refs_it = associations.find(disguised);
if (refs_it != associations.end()) {
auto &refs = refs_it->second;
auto it = refs.find(key);
if (it != refs.end()) {
association.swap(it->second);
refs.erase(it);
if (refs.size() == 0) {
associations.erase(refs_it);
}
}
}
}
}
// release the old value (outside of the lock).
association.releaseHeldValue();
}
可以看到所有的关联对象都是由AssociationsManager
这个对象管理的
class AssociationsManager {
using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>;
static Storage _mapStorage;
public:
AssociationsManager() { AssociationsManagerLock.lock(); }
~AssociationsManager() { AssociationsManagerLock.unlock(); }
AssociationsHashMap &get() {
return _mapStorage.get();
}
static void init() {
_mapStorage.init();
}
};
AssociationsManager
里面有一个静态的_mapStorage
,他是一个ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>
化名为Storage
这样的类型,这里面存放的是AssociationsHashMap
一个键值对的结构,可通过get()
方法取到地址
typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;
AssociationsHashMap 类型解释如下
- key : 是被关联对象的地址
DisguisedPtr<objc_object>
- value : 是另一个
ObjectAssociationMap
,也是个键值对
typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap;
ObjectAssociationMap 类型解释如下
- key : 被关联对象名字的指针
- value : 这是个包含关联对象值和协议的类实例
ObjcAssociation
class ObjcAssociation {
uintptr_t _policy;
id _value;
public:
ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
ObjcAssociation() : _policy(0), _value(nil) {}
ObjcAssociation(const ObjcAssociation &other) = default;
ObjcAssociation &operator=(const ObjcAssociation &other) = default;
ObjcAssociation(ObjcAssociation &&other) : ObjcAssociation() {
swap(other);
}
inline void swap(ObjcAssociation &other) {
std::swap(_policy, other._policy);
std::swap(_value, other._value);
}
inline uintptr_t policy() const { return _policy; }
inline id value() const { return _value; }
inline void acquireValue() {
if (_value) {
switch (_policy & 0xFF) {
case OBJC_ASSOCIATION_SETTER_RETAIN:
_value = objc_retain(_value);
break;
case OBJC_ASSOCIATION_SETTER_COPY:
_value = ((id(*)(id, SEL))objc_msgSend)(_value, @selector(copy));
break;
}
}
}
inline void releaseHeldValue() {
if (_value && (_policy & OBJC_ASSOCIATION_SETTER_RETAIN)) {
objc_release(_value);
}
}
inline void retainReturnedValue() {
if (_value && (_policy & OBJC_ASSOCIATION_GETTER_RETAIN)) {
objc_retain(_value);
}
}
inline id autoreleaseReturnedValue() {
if (slowpath(_value && (_policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE))) {
return objc_autorelease(_value);
}
return _value;
}
};
其具体对应的结构图如下
三、关联属性的销毁
在OC对象的dealloc逻辑里面
void
_object_remove_assocations(id object)
{
ObjectAssociationMap refs{};
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
if (i != associations.end()) {
refs.swap(i->second);
associations.erase(i);
}
}
// release everything (outside of the lock).
for (auto &i: refs) {
i.second.releaseHeldValue();
}
}
可以看到在OC对象销毁的时候,会判断有没有关联属性,如果存在关联属性会将关联属性全部移除掉,所以不需要我们特别的做清理工作。