关联对象

文章目录
  1. 1. 关联对象是什么
  2. 2. 关联对象的主要作用
  3. 3. 如何使用关联对象呢
    1. 3.1. 关联对象接口
  4. 4. 实际使用效果
  5. 5. 关联对象源码分析
    1. 5.1. 关联对象何时释放
    2. 5.2. 关联时采用的协议
  6. 6. 实际中使用关联对象的例子

关联对象是什么

  • 他是 objective-c runtime 机制中提供的一个接口,让用户在运行时,动态的给类添加关联属性

关联对象的主要作用

开发中如何给 category 添加成员属性呢?或者说,如何在运行时给 class 关联新的属性?

  • 主要作用:给category 添加成员变量
    • category 底层结构的限制,不能添加成员变量到分类中。但可以通过关联对象来间接添加。
    • 分类中的 @property 只能声明属性,不提供 _name, getName, setName 的实现
    • 类中 @property 提供 _name, getName, setName 的实现

如何使用关联对象呢

关联对象接口

<objc/runtime.h> 中定义的以下三个允许你将任何键值在运行时关联到对象上的函数:

  • objc_setAssociatedObject
  • objc_getAssociatedObject
  • objc_removeAssociatedObjects
1
2
3
4
5
6
7
8
9
10
/*
object :表示关联者,是一个对象,变量名理所当然也是object
key :获取被关联者的索引key
value :被关联者
policy :关联时采用的协议,有assign,retain,copy等协议,一般使用OBJC_ASSOCIATION_RETAIN_NONATOMIC
*/
id objc_getAssociatedObject(id object, const void *key);
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
//移除某个对象身上的所有关联的对象。
void objc_removeAssociatedObjects(id object)

实际使用效果

对比字典实现给分类添加 property

1
2
3
4
@interface Banana (Test)
@property (assign, nonatomic) CGFloat weight;
@property (copy, nonatomic) NSString *name;
@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#define Key [NSString stringWithFormat:@"%p", self]
@implementation Banana (Test)
NSMutableDictionary *names_;
NSMutableDictionary *weights_;
+ (void)load {
weights_ = [NSMutableDictionary dictionary];
names_ = [NSMutableDictionary dictionary];
}
- (void)setName:(NSString *)name {
names_[Key] = name;
}
- (NSString *)name {
return names_[Key];
}
- (void)setWeight:(int)weight {
weights_[Key] = @(weight);
}
- (int)weight {
return [weights_[Key] intValue];
}
@end

上面使用全局字典的方式,给分类中的成员变量添加get,set 实现体

那么如何使用关联对象呢?

  • 关联对象实现分类添加成员属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@implementation Banana (Test)
- (void)setName:(NSString *)name{
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
// 隐式参数_cmd == @selector(name)
return objc_getAssociatedObject(self, _cmd);
}
- (void)setWeight:(int)weight {
objc_setAssociatedObject(self, @selector(weight), @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (int)weight {
return [objc_getAssociatedObject(self, _cmd) intValue];
}
@end

关联对象源码分析

关联对象也是用全局 hash 表来保存的。
objc4-779.1源码 这个文件中 objc-references.mm

关联对象的核心类

  • AssociationsManager
  • AssociationsHashMap
  • ObjectAssociationMap
  • ObjcAssociation
1
2
3
4
5
6
7
8
9
10
class AssociationsManager {
// associative references: object pointer -> PtrPtrHashMap.
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;
};

关联对象何时释放

…… 不想看了,想看了再补充,总之他在所关联的对象释放后,会释放的

关联时采用的协议

Behavior@property EquivalentDescription
OBJC_ASSOCIATION_ASSIGN@property (assign) 或 @property (unsafe_unretained)指定一个关联对象的弱引用。
OBJC_ASSOCIATION_RETAIN_NONATOMIC@property (nonatomic, strong)指定一个关联对象的强引用,不能被原子化使用。
OBJC_ASSOCIATION_COPY_NONATOMIC@property (nonatomic, copy)指定一个关联对象的copy引用,不能被原子化使用。
OBJC_ASSOCIATION_RETAIN@property (atomic, strong)指定一个关联对象的强引用,能被原子化使用。
OBJC_ASSOCIATION_COPY@property (atomic, copy)指定一个关联对象的copy引用,能被原子化使用。

实际中使用关联对象的例子

使用 closures 添加 Gesture Recognizers