Category和关联对象
Category也就是分类,可以在没有类定义的情况下为现有类添加方法、属性、协议,众所周知是不能增加成员变量的,但是是可以通过关联对象给类增加的。
struct category_t {
const char *name;
classref_t cls;
WrappedPtr<method_list_t, method_list_t::Ptrauth> instanceMethods;
WrappedPtr<method_list_t, method_list_t::Ptrauth> 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;
}
};
可以看出,这里面只有方法、协议、属性列表,并没有存储成员变量的地方。所以就用到了关联对象去存储这些数据。
关联对象主要通过以下三个函数进行实现
//设置
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
_object_set_associative_reference(object, key, value, policy);
}
//获取
id objc_getAssociatedObject(id object, const void *key)
{
return _object_get_associative_reference(object, key);
}
//删除
void objc_removeAssociatedObjects(id object)
{
if (object && object->hasAssociatedObjects()) {
_object_remove_associations(object, /*deallocating*/false);
}
}
先看下objc_AssociationPolicy 策略的定义
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, /**< Specifies a weak reference to the associated object. */
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
* The association is not made atomically. */
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied.
* The association is not made atomically. */
OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object.
* The association is made atomically. */
OBJC_ASSOCIATION_COPY = 01403 /**< Specifies that the associated object is copied.
* The association is made atomically. */
};
这里分别提供了weak、strong、copy对应的策略。怎么使用这里就不多说明了,主要看下内部是怎么实现的。
objc_setAssociatedObject方法里面调用了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();
先判断对象和设置的值是否为空,空的话直接返回。再判断对象是否禁止设置关联对象。DisguisedPtr类的disguised指针,DisguisedPtr是为了是为了躲过内存泄漏工具的检查。然后用policy和valus初始化ObjcAssociation类型对象,再调用对象的acquireValue方法。
class ObjcAssociation {
uintptr_t _policy;
id _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;
}
}
}
}
可以看到,这里是根据策略执行value的retain还是copy。
bool isFirstAssociation = false;
{
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 */
isFirstAssociation = true;
}
/* 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);
}
}
}
}
}
1、定义是否是第一次设置关联对象isFirstAssociation = false
2、获取AssociationsManager,然后通过manager获取全局AssociationsHashMap
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();
}
};
可以看出AssociationsHashMap是static的 Storage获取的,全局唯一的。
3、关联对象如果存在,从AssociationsHashMap中通过对象指针获取ObjectAssociationMap
template <typename... Ts>
std::pair<iterator, bool> try_emplace(const KeyT &Key, Ts &&... Args) {
BucketT *TheBucket;
if (LookupBucketFor(Key, TheBucket))
return std::make_pair(
makeIterator(TheBucket, getBucketsEnd(), true),
false); // Already in map.
// Otherwise, insert the new element.
TheBucket = InsertIntoBucket(TheBucket, Key, std::forward<Ts>(Args)...);
return std::make_pair(
makeIterator(TheBucket, getBucketsEnd(), true),
true);
}
如果之前设置过,通过查找获取后返回,不存在创建后返回。
4、判断如果是第一次设置关联对象,将isFirstAssociation赋值true
5、再将ObjcAssociation存储到ObjectAssociationMap中,以key作为键值。
6、如果value不存在的时候,通过对象指针从hashmap中获取map,然后再通过key从map中获取object,如果存在就erase,如果map的size为空也erase掉。
if (isFirstAssociation)
object->setHasAssociatedObjects();
// release the old value (outside of the lock).
association.releaseHeldValue();
}
这部分就比较简单了,如果是第一次设置关联对象,就把对象的 newisa.has_assoc设置为true,方便对象释放的时候同时释放关联对象,最后release旧的value。
再看下objc_getAssociatedObject里面是调用了_object_get_associative_reference方法
id
_object_get_associative_reference(id object, const void *key)
{
ObjcAssociation association{};
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
if (i != associations.end()) {
ObjectAssociationMap &refs = i->second;
ObjectAssociationMap::iterator j = refs.find(key);
if (j != refs.end()) {
association = j->second;
association.retainReturnedValue();
}
}
}
return association.autoreleaseReturnedValue();
}
这里很直观的可以看出,先声明了个ObjcAssociation,然后创建AssociationsManager,再获取AssociationsHashMap,再通过AssociationsHashMap以object为key获取ObjectAssociationMap,再以key获取ObjcAssociation,然后返回获取到的值。
综上可看出,存储结构如下图所示