CC的博客

  • 首页
  • iOS
  • Android
  • React-Native
  • 读书杂谈
  • About
CC
记录美好生活
  1. 首页
  2. 技术编程
  3. iOS
  4. 正文

探究Category-02添加成员变量

2021/06/05

背景

定义一个Student类,使用@property声明一个score属性,编译器会自动帮我们生成_score成员变量和对应的setter&getter方法声明和实现。

@interface Student 

//@property (nonatomic,assign) int score;
{
    int _score;
}
-(void)setScore:(int)score;
-(int)score;
@end

@implementation Student 
- (void)setScore:(int)score {
    _score = score;
}
- (int)score {
    return  _score;
}
@end

在分类中,使用@property声明一个score属性,编译器只会帮我们声明setter&getter方法。

@interface Student (test)
//@property (nonatomic,assign) int score;
//等同于下面的代码
-(void)setScore:(int)score;
-(int)score;
@end

@implementation Student (test)

@end

Xcode会发出方法未实现的警告⚠️

Property 'score' requires method 'score' to be defined - use @dynamic or provide a method implementation in this category

Property 'score' requires method 'setScore:' to be defined - use @dynamic or provide a method implementation in this category

如何添加成员变量

由于分类底层结构的限制,不能添加成员变量到分类数据中,但可以用关联对象来实现。

关联对象的API。

添加关联对象
void objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
                         id _Nullable value, objc_AssociationPolicy policy)
获取关联对象
id _Nullable objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
移除所有的关联对象
void objc_removeAssociatedObjects(id _Nonnull object)

关联对象key的用法

  • 使用指针作为key
static void *MyKey = &MyKey;
objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, MyKey)
  • 使用指针地址作为key
static char MyKey;
objc_setAssociatedObject(obj, &MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, &MyKey)
  • 使用属性名作为key
objc_setAssociatedObject(obj, @"property", value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_getAssociatedObject(obj, @"property");
  • 使用get方法的@selecor作为key
objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, @selector(getter))

objc_AssociationPolicy内存管理策略

内存管理策略对应的修饰符,如下所示:

OBJC_ASSOCIATION_ASSIGN == assign  
OBJC_ASSOCIATION_RETAIN_NONATOMIC == strong,nonatomic 
OBJC_ASSOCIATION_COPY_NONATOMIC  == copy,nonatomic
OBJC_ASSOCIATION_RETAIN  == strong,atomic
OBJC_ASSOCIATION_COPY == copy,atomic

用关联对象给category添加成员变量的示例代码:

@interface Student (test)
@property (nonatomic,assign) int score;
@end

@implementation Student (test)

- (void)setScore:(int)score {
    objc_setAssociatedObject(self, @selector(score), @(score), OBJC_ASSOCIATION_ASSIGN);
}

- (int)score {

    return [objc_getAssociatedObject(self, @selector(score)) intValue];
}

@end

关联对象的原理

全局的AssociationsManager内部有个AssociationsHashMap,AssociationsHashMap内部以object转换后值作为key,存储着ObjectAssociationMap对象,ObjectAssociationMap对象内部以外部传进来key存储着ObjcAssociation对象,ObjcAssociation对象内存储着关联值和关联值的内存管理策略。

class AssociationsManager {
    static AssociationsHashMap *_map;
    ...
};


class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {...};

class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {...};

class ObjcAssociation {
    uintptr_t _policy;
    id _value;
    ...
};

artboard 14.png

查看源码定义

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 old_association(0, nil);
    id new_value = value ? acquireValue(value, policy) : nil;
    {
        AssociationsManager manager;
        //取出manager的AssociationsHashMap
        AssociationsHashMap &associations(manager.associations());
        //根据object生成key
        disguised_ptr_t disguised_object = DISGUISE(object);
        //value不为空
        if (new_value) {
            // break any existing association.
            //迭代manager的AssociationsHashMap
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i != associations.end()) {
                // secondary table exists
                ObjectAssociationMap *refs = i->second;
                //根据key查找AssociationsHashMap内存储的ObjectAssociationMap
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    //生成一个ObjcAssociation并存储
                    j->second = ObjcAssociation(policy, new_value);
                } else {
                    //生成一个ObjcAssociation并存储
                    (*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 {
            //value为nil,就删除对应的ObjcAssociation
            // 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);
}
标签: 暂无
最后更新:2024/07/06

CC

这个人很懒,什么都没留下

点赞
< 上一篇
下一篇 >

COPYRIGHT © 2020 CC的博客. ALL RIGHTS RESERVED.

Theme Kratos

豫ICP备2023032048号