OC中给分类添加成员变量
在OC中,由于分类底层结构的限制,并不能添加成员变量到分类中,但是可以通过关联对象实现。
涉及到的相关api
//添加关联对象 // objece 需要添加关联对象的对象 // key 用来去关联值得key // value 关联的值 // policy 政策(retain、assin) void objc_setAssociatedObject(id object, const void * key, id value, objc_AssociationPolicy policy)
// 获取关联对象 // object 需要获取关联对象的对象 // key 用来取关联对象的key id objc_getAssociatedObject(id object, const void * key)
// 溢出对象的所有关联对象 void objc_removeAssociatedObjects(id object)
实现为分类添加"属性的代码如下":
#import <Foundation/Foundation.h> #import "LBPerson.h" #import "LBPerson+Associate.h" int main(int argc, const char * argv[]) { @autoreleasepool { LBPerson *person = [[LBPerson alloc] init]; person.name = @"muzi"; NSLog(@"%@",person.name); } return 0; }
#import "LBPerson.h" NS_ASSUME_NONNULL_BEGIN @interface LBPerson (Associate) @property (nonatomic, copy) NSString *name; @end #import "LBPerson+Associate.h" #import <objc/runtime.h> @implementation LBPerson (Associate) - (void)setName:(NSString *)name { // OBJC_ASSOCIATION_ASSIGN = 0, assign // OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, strong, nonatomic // OBJC_ASSOCIATION_COPY_NONATOMIC = 3, copy, nonatomic // OBJC_ASSOCIATION_RETAIN = 01401, strong, atomic // OBJC_ASSOCIATION_COPY = 01403 copy, atomic objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_COPY); } - (NSString *)name { return objc_getAssociatedObject(self, @"name"); } @end
设置关联对象的源码如下:
typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap; typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap; // class AssociationsManager manages a lock / hash table singleton pair. // Allocating an instance acquires the lock class AssociationsManager { using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>; static Storage _mapStorage; public: AssociationsManager() { AssociationsManagerLock.lock(); } ~AssociationsManager() { AssociationsManagerLock.unlock(); } AssociationsHashMap &get() { return _mapStorage.get(); } static void init() { _mapStorage.init(); } }; AssociationsManager::Storage AssociationsManager::_mapStorage; } // namespace objc using namespace objc; void _objc_associations_init() { AssociationsManager::init(); } id _object_get_associative_reference(id object, const void *key) { ObjcAssociation association{}; { AssociationsManager manager; // 获取AssociationsManaget的对象 AssociationsHashMap &associations(manager.get()); // return _mapStorage.get();
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
if (i != associations.end()) { ObjectAssociationMap &refs = i->second; ObjectAssociationMap::iterator j = refs.find(key); if (j != refs.end()) { association = j->second; association.retainReturnedValue(); } } } return association.autoreleaseReturnedValue(); } void _object_set_associative_reference(id object, const void *key, id value, uintptr_t policy) { // This code used to work when nil was passed for object and key. Some code // probably relies on that to not crash. Check and handle it explicitly. // rdar://problem/44094390 if (!object && !value) return; if (object->getIsa()->forbidsAssociatedObjects()) _objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object)); DisguisedPtr<objc_object> disguised{(objc_object *)object}; ObjcAssociation association{policy, value}; // retain the new value (if any) outside the lock. association.acquireValue(); { AssociationsManager manager; AssociationsHashMap &associations(manager.get()); if (value) { auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{}); if (refs_result.second) { /* it's the first association we make */ object->setHasAssociatedObjects(); } /* establish or replace the association */ auto &refs = refs_result.first->second; auto result = refs.try_emplace(key, std::move(association)); if (!result.second) { association.swap(result.first->second); } } else { auto refs_it = associations.find(disguised); if (refs_it != associations.end()) { auto &refs = refs_it->second; auto it = refs.find(key); if (it != refs.end()) { association.swap(it->second); refs.erase(it); if (refs.size() == 0) { associations.erase(refs_it); } } } } } // release the old value (outside of the lock). association.releaseHeldValue(); } // Unlike setting/getting an associated reference, // this function is performance sensitive because of // raw isa objects (such as OS Objects) that can't track // whether they have associated objects. void _object_remove_assocations(id object) { ObjectAssociationMap refs{}; { AssociationsManager manager; AssociationsHashMap &associations(manager.get()); AssociationsHashMap::iterator i = associations.find((objc_object *)object); if (i != associations.end()) { refs.swap(i->second); associations.erase(i); } } // release everything (outside of the lock). for (auto &i: refs) { i.second.releaseHeldValue(); } }
本来还想注释一波源码,奈何C++学的太菜,大致总结如下:
与关联对象有关的四个
AssociationsManager
AssociationsHashMap
ObjectAssociationMap
ObjcAssociation
大致调用,先取出mannager中的AssociationsHashMap,然后再通过DisguisedPtr作key从AssociationsHashMap取出ObjectAssociationMap,再用key取ObjcAssociation
,ObjcAssociation中放的就是策略和报错的关联值,顺序如下图所示: