美文网首页
OC底层原理学习笔记(四)- 关联对象

OC底层原理学习笔记(四)- 关联对象

作者: hyq1101 | 来源:发表于2022-03-05 16:52 被阅读0次
  • 给类添加属性会自动生成带下划线的成员变量,也会生成setter和getter方法的声明及实现
  • 给分类添加属性不会生成带下划线的成员变量,只会生成setter和getter方法的声明
  • 不能直接给Category添加成员变量,但是可以通过关联对象间接实现Category有成员变量的效果

一、给分类添加属性方法一:用字典存储属性的值

#import "Person.h"
@interface Person (Test)
@property (assign, nonatomic) int weight;
@end

#import "Person+Test.h"
#define WeightKey [NSString stringWithFormat:@"%p", self]
@implementation Person (Test)
NSMutableDictionary *weight_;
+ (void)load {
    weight_ = [NSMutableDictionary dictionary];
}
- (void)setWeight:(int)weight {
    weight_[WeightKey] = @(weight);
}
- (int)weight {
    return [weight_[WeightKey] intValue];
}
@end

缺点:太麻烦,需考虑线程安全等问题

二、给分类添加属性方法二:通过关联对象实现

方式一:用NameKey的内存地址值作为key
static const void *NameKey = &NameKey; //占8个字节
- (void)setName:(NSString *)name {
    objc_setAssociatedObject(self, NameKey, name, 
    OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
    return objc_getAssociatedObject(self, NameKey);
}
方式二:
#define NameKey @"name"
- (void)setName:(NSString *)name {
    objc_setAssociatedObject(self, NameKey, name, 
    OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
    return objc_getAssociatedObject(self, NameKey);
}
方式三:
- (void)setName:(NSString *)name {
    objc_setAssociatedObject(self, @selector(name), name, 
    OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
    //_cmd = @selector(name)
    return objc_getAssociatedObject(self, @selector(name));
    //或者
    //return objc_getAssociatedObject(self, _cmd);
}
方式四:
static const char NameKey; //占1个字节
- (void)setName:(NSString *)name {
    objc_setAssociatedObject(self, &NameKey, name, 
    OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
    return objc_getAssociatedObject(self, &NameKey);
}

三、关联对象的原理

  • 关联对象并不是存储在被关联对象本身内存中
  • 关联对象存储在全局的统一的一个AssociationManager中
  • 设置关联对象为nil,就相当于是移除关联对象
源码 objc-runtime.mm
// 取值
id objc_getAssociatedObject(id object, const void *key) {
    return _object_get_associative_reference(object, (void *)key);
}

// 赋值
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
    _object_set_associative_reference(object, (void *)key, value, policy);
}

源码 objc-references.mm
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;
}

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);
}

相关文章

网友评论

      本文标题:OC底层原理学习笔记(四)- 关联对象

      本文链接:https://www.haomeiwen.com/subject/eplbrrtx.html