背景
定义一个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;
...
};
查看源码定义
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);
}