前一段时间恶补了一些 C++ 的相关知识, 看的越多看得越深发现 iOS 相关实现也越来越简单, 昨天看了一下关于关联对象存取的实现原理, 现整理出来供以后查阅
ps: 用到的是 objc_703.zip 源码包
先贴一张图, 再结合源码, 其中原理实现自当一目了然~
大致实现图. png
以下贴出来的源码, 是经过轻微简化小范围宏展开之后的代码, 源码中关键地方做了简单的注释, 不太严谨或者不正确的地方还请斧正, 大家一起学习进步
objc-references.h 头文件
1. 包含了必要的引用文件
2. 关联对象存取的三个方法:
_object_set_associative_reference 设置关联对象,_object_get_associative_reference 获取关联对象,
_object_remove_assocations 删除关联对象
- /*************************Begin***************************/
- #ifndef _OBJC_REFERENCES_H_
- #define _OBJC_REFERENCES_H_
- #include "objc-api.h"
- #include "objc-config.h"
- extern "C" {
- extern void _object_set_associative_reference(id object, void *key, id value, unsigned long policy);
- extern id _object_get_associative_reference(id object, void *key);
- extern void _object_remove_assocations(id object);
- }
- #endif
- /*************************End*****************************/
objc-references.m 实现
1. 包含头文件中声明的三个方法的实现
2. 相关的辅助方法
- /*************************Begin***************************/
- #include "objc-private.h"
- #include <objc/message.h>
- #include <map>
- include <unordered_map>
- // 将所有隐藏的 C++ 细节包装在一个命名空间中
- namespace objc_references_support {
- // 这是一个函数对象, 用于比较两个对象指针值 (已按位取反, 转化为 unsigned long 类型)
- // 可以将这个函数对象理解为一个二元谓词 (返回值 bool, 两个参数)
- struct DisguisedPointerEqual {
- bool operator()(unsigned long p1, unsigned long p2) const {
- return p1 == p2;
- }
- };
- struct DisguisedPointerHash {
- unsigned long operator()(unsigned long k) const {
- // borrowed from CFSet.c
- #if __LP64__
- unsigned long a = 0x4368726973746F70ULL;
- unsigned long b = 0x686572204B616E65ULL;
- #else
- unsigned long a = 0x4B616E65UL;
- unsigned long b = 0x4B616E65UL;
- #endif
- unsigned long c = 1;
- a += k;
- #if __LP64__
- a -= b; a -= c; a ^= (c >> 43);
- b -= c; b -= a; b ^= (a << 9);
- c -= a; c -= b; c ^= (b >> 8);
- a -= b; a -= c; a ^= (c >> 38);
- b -= c; b -= a; b ^= (a << 23);
- c -= a; c -= b; c ^= (b >> 5);
- a -= b; a -= c; a ^= (c >> 35);
- b -= c; b -= a; b ^= (a << 49);
- c -= a; c -= b; c ^= (b >> 11);
- a -= b; a -= c; a ^= (c >> 12);
- b -= c; b -= a; b ^= (a << 18);
- c -= a; c -= b; c ^= (b >> 22);
- #else
- a -= b; a -= c; a ^= (c >> 13);
- b -= c; b -= a; b ^= (a << 8);
- c -= a; c -= b; c ^= (b >> 13);
- a -= b; a -= c; a ^= (c >> 12);
- b -= c; b -= a; b ^= (a << 16);
- c -= a; c -= b; c ^= (b >> 5);
- a -= b; a -= c; a ^= (c >> 3);
- b -= c; b -= a; b ^= (a << 10);
- c -= a; c -= b; c ^= (b >> 15);
- #endif
- return c;
- }
- };
- // 容器排列从小到大
- struct ObjectPointerLess {
- bool operator()(const void *p1, const void *p2) const {
- return p1 < p2;
- }
- };
- // 获取一个对象的哈希值
- struct ObjcPointerHash {
- unsigned long operator()(void *p) const {
- return DisguisedPointerHash()(unsigned long(p));
- }
- };
- // STL allocator that uses the runtime's internal allocator.
- template <typename T> struct ObjcAllocator {
- typedef T value_type;
- typedef value_type* pointer;
- typedef const value_type *const_pointer;
- typedef value_type& reference;
- typedef const value_type& const_reference;
- typedef size_t size_type;
- typedef ptrdiff_t difference_type;
- template <typename U> struct rebind { typedef ObjcAllocator<U> other; };
- template <typename U> ObjcAllocator(const ObjcAllocator<U>&) {}
- ObjcAllocator() {}
- ObjcAllocator(const ObjcAllocator&) {}
- ~ObjcAllocator() {}
- pointer address(reference x) const { return &x; }
- const_pointer address(const_reference x) const {
- return x;
- }
- pointer allocate(size_type n, const_pointer = 0) {
- return static_cast<pointer>(::malloc(n * sizeof(T)));
- }
- void deallocate(pointer p, size_type) { ::free(p); }
- size_type max_size() const {
- return static_cast<size_type>(-1) / sizeof(T);
- }
- void construct(pointer p, const value_type& x) {
- new(p) value_type(x);
- }
- void destroy(pointer p) { p->~value_type(); }
- void operator=(const ObjcAllocator&);
- };
- template<> struct ObjcAllocator<void> {
- typedef void value_type;
- typedef void* pointer;
- typedef const void *const_pointer;
- template <typename U> struct rebind { typedef ObjcAllocator<U> other; };
- };
- typedef unsigned long disguised_ptr_t;
- inline disguised_ptr_t DISGUISE(id value) { return ~unsigned long(value); }
- inline id UNDISGUISE(disguised_ptr_t dptr) { return id(~dptr); }
- class ObjcAssociation {
- unsigned long _policy;
- id _value;
- public:
- ObjcAssociation(unsigned long policy, id value) : _policy(policy), _value(value) {}
- ObjcAssociation() : _policy(0), _value(nil) {}
- unsigned long policy() const { return _policy; }
- id value() const { return _value; }
- bool hasValue() { return _value != nil; }
- };
- #if TARGET_OS_WIN32
- typedef hash_map<void *, ObjcAssociation> ObjectAssociationMap;
- typedef hash_map<disguised_ptr_t, ObjectAssociationMap *> AssociationsHashMap;
- #else
- typedef ObjcAllocator<std::pair<void * const, ObjcAssociation> > ObjectAssociationMapAllocator;
- // 地址值从小到大
- class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {
- public:
- void *operator new(size_t n) { return ::malloc(n); }
- void operator delete(void *ptr) { ::free(ptr); }
- };
- typedef ObjcAllocator<std::pair<const disguised_ptr_t, ObjectAssociationMap*> > AssociationsHashMapAllocator;
- // key 唯一
- class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {
- public:
- void *operator new(size_t n) { return ::malloc(n); }
- void operator delete(void *ptr) { ::free(ptr); }
- };
- #endif
- }
- using namespace objc_references_support;
- // AssociationsManager 类 管理了着一个锁还有一个全局的哈希键值对表.
- // 初始化一个 AssociationsManager 实例对象的时候, 会获取一个分配了内存的锁, 并且调用它的 assocations() 方法来懒加载获取哈希表
- spinlock_t AssociationsManagerLock;
- class AssociationsManager {
- // associative references: object pointer -> PtrPtrHashMap.
- static AssociationsHashMap *_map;
- public:
- AssociationsManager() { AssociationsManagerLock.lock(); }
- ~AssociationsManager() { AssociationsManagerLock.unlock(); }
- AssociationsHashMap &associations() {
- if (_map == NULL)
- _map = new AssociationsHashMap();
- return *_map;
- }
- };
- AssociationsHashMap *AssociationsManager::_map = NULL;
- enum {
- OBJC_ASSOCIATION_SETTER_ASSIGN = 0,
- OBJC_ASSOCIATION_SETTER_RETAIN = 1,
- OBJC_ASSOCIATION_SETTER_COPY = 3,
- OBJC_ASSOCIATION_GETTER_READ = (0 << 8),
- OBJC_ASSOCIATION_GETTER_RETAIN = (1 << 8),
- OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8)
- };
- // 根据一个给定的对象以及一个键值来获取它所绑定过的关联对象
- id _object_get_associative_reference(id object, void *key) {
- id value = nil;// 关联对象
- unsigned long policy = OBJC_ASSOCIATION_ASSIGN;// 缓存策略
- {// 安全释放
- AssociationsManager manager;// 初始化 AssociationsManager 管理类
- /*
- 这里用到了 C++ 中引用的知识点, 对这一块不太了解的同学可以自行查阅
- int a = 10;
- int &b = a;
- b 变量只是 a 的别名, 两者访问的是同一块内存空间
- 为了更好的理解这里我们拆分一下
- AssociationsHashMap &associations(manager.associations());
- manager.associations();manager 的 associations() 方法返回的是引用, 是一个全局哈希键值对表的引用, 我们一个临时变量去接这个返回值
- */
- AssociationsHashMap tmp = manager.associations();
- AssociationsHashMap &associations(tmp);//AssociationsHashMap 引用初始化方法
- /*
- http://blog.csdn.net/coder__cs/article/details/79186677
- 阐述了计算机能够识别的二进制串是一种补码, 我们理解的原码反码只是我们自己定义的, 计算机并不能识别
- 二进制按位取反 x 的按位取反结果为 -(x+1)
- */
- unsigned long disguised_object = DISGUISE(object);
- /*
- ObjectAssociationMap stl 中 map 的子类保存的是键值对
- 绑定关联对象时用到的 key 作为键, ObjcAssociation(policy,new_value) 作为值, 这样一对键值对存储在 ObjectAssociationMap 中
- AssociationsHashMap stl 中 unordered_map 的子类, 这里是全局的
- 以对象指针 16 进制按位取反获得的补码作为键, 以上述 ObjectAssociationMap 作为值, 存储在 AssociationsHashMap 中
- */
- /*
- unordered_map 容器中的迭代器, 在 AssociationsHashMap 中查找给定对象所有绑定对象的键值对
- 查找到之后返回容器中所在的位置, 用迭代器指向所在的位置
- i->first; //key
- i->second; // value
- */
- 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) ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);
- }
- }
- }
- // 根据缓存策略来对关联对象进行内存管理
- if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
- ((id(*)(id, SEL))objc_msgSend)(value, SEL_autorelease);
- }
- return value;
- }
- static id acquireValue(id value, unsigned long policy) {
- switch (policy & 0xFF) {
- case OBJC_ASSOCIATION_SETTER_RETAIN:
- return ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);
- case OBJC_ASSOCIATION_SETTER_COPY:
- return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
- }
- return value;
- }
- static void releaseValue(id value, unsigned long policy) {
- if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
- ((id(*)(id, SEL))objc_msgSend)(value, SEL_release);
- }
- }
- struct ReleaseValue {
- void operator() (ObjcAssociation &association) {
- releaseValue(association.value(), association.policy());
- }
- };
- // 给某个 object 绑定关联对象
- void _object_set_associative_reference(id object, void *key, id value, unsigned long policy) {
- // retain the new value (if any) outside the lock.
- ObjcAssociation old_association(0, nil);
- // value 对象时简单的技术加一还是 copy
- 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
- /*
- i->first; key disguised_object
- i->second;value ObjectAssociationMap
- */
- ObjectAssociationMap *refs = i->second;
- ObjectAssociationMap::iterator j = refs->find(key);
- if (j != refs->end()) {
- /*
- i->first; key 就是绑定关联对象时用到的 key 作为键值
- i->second;value 就是 ObjcAssociation(policy, new_value)
- */
- 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);
- }
- // 删除 object 对象绑定的关联对象
- void _object_remove_assocations(id object) {
- vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
- // 搭建舞台, 生命周期完整
- {
- // AssociationsManager 无参构造函数以及析构函数分别实现了加锁解锁操作, 避免了资源竞争
- // AssociationsManager 内部有一个共享的 AssociationsHashMap
- AssociationsManager manager;
- // AssociationsHashMap 集成 unordered_map 无序 map
- // unordered_map(const unordered_map& __u);
- // manager.associations() 返回值 AssociationsHashMap 引用
- AssociationsHashMap tmp = manager.associations();
- AssociationsHashMap &associations(tmp);
- if (associations.size() == 0) return;
- /*
- inline
- disguised_ptr_t DISGUISE(id value) {
- return ~unsigned long(value);
- }
- unsigned long DISGUISE(id value) {
- return ~(unsigned long)(value);
- }
- */
- disguised_ptr_t disguised_object = DISGUISE(object);
- // 迭代器
- AssociationsHashMap::iterator i = associations.find(disguised_object);
- if (i != associations.end()) {
- // copy all of the associations that need to be removed.
- ObjectAssociationMap *refs = i->second;
- for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
- elements.push_back(j->second);
- }
- // remove the secondary table.
- delete refs;
- associations.erase(i);
- }
- }
- // the calls to releaseValue() happen outside of the lock.
- /*
- struct ReleaseValue {
- void operator() (ObjcAssociation &association) {
- releaseValue(association.value(), association.policy());
- }
- }
- */
- // 遍历对象, 对容器中的所有关联对象进行内存释放
- for_each(elements.begin(), elements.end(), ReleaseValue());
- }
- /*************************End*****************************/
测试代码, 一个很简单的老师类, 里面没有任何属性, 方法我们就是想给这个老师绑定姓名年龄任职学校, 如何做呢? 用关联对象
- //
- // main.m
- // debug_objc
- //
- // Created by wwh on 19/09/2017.
- //
- #import <Foundation/Foundation.h>
- #import "objc-runtime.h"
- static char *kTeacherAgeKey = "kTeacherKey";
- static char *kTeacherNameKey = "kTeacherNameKey";
- static char *kTeacherSchoolKey = "kTeacherSchoolKey";
- @interface Teacher: NSObject
- @end
- @implementation Teacher
- @end
- int main(int argc, const char * argv[]) {
- @autoreleasepool {
- // insert code here...
- Teacher *teacher = [Teacher new];
- objc_setAssociatedObject(teacher, kTeacherAgeKey, @(28), OBJC_ASSOCIATION_RETAIN);
- objc_setAssociatedObject(teacher, kTeacherNameKey, @"王某某", OBJC_ASSOCIATION_COPY);
- objc_setAssociatedObject(teacher, kTeacherSchoolKey, @"北京大学", OBJC_ASSOCIATION_COPY);
- objc_setAssociatedObject(teacher, kTeacherSchoolKey, @"清华大学", OBJC_ASSOCIATION_COPY);
- NSString *name = (NSString *)objc_getAssociatedObject(teacher, kTeacherNameKey);
- NSString *school = (NSString *)objc_getAssociatedObject(teacher, kTeacherSchoolKey);
- NSNumber *age = (NSNumber *)objc_getAssociatedObject(teacher, kTeacherAgeKey);
- NSLog(@"name=%@,age=%@,school=%@", name, age, school);
- }
- return 0;
- }
如果你讨厌看源码的话, 直接看总结吧:
关联对象的实现其实很简单的, 对于一个给定的对象, 设置关联对象时的存储结构是这样子的:
存储结构图. png
解释:
给定对象 1 的内存地址的按位取反获得的补码作为键 这句话可以理解成 Teacher 实例对象对象;
ObjectAssociationMap 可以理解成一个字典;
而这个字典保存的内容就是:
年龄 = 28 姓名 = 王某某任职学校 = 清华大学三个键值对
来源: http://www.jianshu.com/p/f98fce9ab576