前面几篇我们探索了类
的加载过程,本篇我们研究类相关的两个点:类的扩展
和关联对象
。
类扩展 clang编译 我们首先在main.m
文件中新建一个类JSAnimal
,并给类定义扩展,注意扩展
要在声明之后
和实现之后
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 @interface JSAnimal : NSObject @property (nonatomic,copy)NSString *name; - (void)sayWow; @end @interface JSAnimal () @property (nonatomic,copy)NSString *type; - (void)ex_sayWow; @end @implementation JSAnimal + (void)classMethod{ NSLog(@"%s",__func__); } - (void)sayWow{ NSLog(@"%s",__func__); } - (void)ex_sayWow{ NSLog(@"%s",__func__); } @end int main(int argc, const char * argv[]) { @autoreleasepool { JSAnimal *animal = [[JSAnimal alloc] init]; [animal ex_sayWow]; NSLog(@"done"); } return 0; }
我们使用clang
命令,将main.m
转成main.cpp
文件,看一下分类的实现
1 clang -rewrite-objc main.m -o main.cpp
main.cpp
文件搜索JSAnimal
发现扩展
里声明的属性
和方法
编译后和类
中的在一起,作为类
的一部分,也就是说扩展中的属性和方法
在编译期
就添加到本类
中了。
通过源码探索运行时 定义一个JSPerson
类和扩展,本类中实现扩展中声明的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @interface JSPerson : NSObject @property (nonatomic, copy) NSString *name; - (void)saySomething; @end #import "JSPerson.h" @implementation JSPerson + (void)load{ } - (void)saySomething{ NSLog(@"%s",__func__); } - (void)ext_instanceMethod{ NSLog(@"%s",__func__); } + (void)ext_classMethod{ NSLog(@"%s",__func__); } @end
1 2 3 4 5 6 7 @interface JSPerson () - (void)ext_instanceMethod; + (void)ext_classMethod; @end
注意JSPerson
类实现了load
方法,目的是让其非懒加载
。根据我们的经验,我们在realizeClassWithoutSwift
添加断点调试
通过lldb
打印ro
中的方法列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 (lldb) p ro (const class_ro_t *) $0 = 0x0000000100004790 (lldb) p *$0 (const class_ro_t ) $1 = { flags = 0 instanceStart = 8 instanceSize = 16 reserved = 0 = { ivarLayout = 0x0000000000000000 nonMetaclass = nil } name = { std ::__1::atomic<const char *> = "JSPerson" { Value = 0x0000000100003b58 "JSPerson" } } baseMethodList = 0x00000001000047d8 baseProtocols = nil ivars = 0x0000000100004840 weakIvarLayout = 0x0000000000000000 baseProperties = 0x0000000100004868 _swiftMetadataInitializer_NEVER_USE = {} } (lldb) p $1.b aseMethods() (method_list_t *) $2 = 0x00000001000047d8 (lldb) p *$2 (method_list_t ) $3 = { entsize_list_tt<method_t , method_list_t , 4294901763 , method_t ::pointer_modifier> = (entsizeAndFlags = 24 , count = 4 ) } (lldb) p $3. get (0 ).big() (method_t ::big) $4 = { name = "saySomething" types = 0x0000000100003d84 "v16@0:8" imp = 0x0000000100003600 (KCObjcBuild`-[JSPerson saySomething]) } (lldb) p $3. get (1 ).big() (method_t ::big) $5 = { name = "ext_instanceMethod" types = 0x0000000100003d84 "v16@0:8" imp = 0x0000000100003630 (KCObjcBuild`-[JSPerson ext_instanceMethod]) } (lldb) p $3. get (2 ).big() (method_t ::big) $6 = { name = "name" types = 0x0000000100003d98 "@16@0:8" imp = 0x0000000100003660 (KCObjcBuild`-[JSPerson name]) } (lldb) p $3. get (3 ).big() (method_t ::big) $7 = { name = "setName:" types = 0x0000000100003da0 "v24@0:8@16" imp = 0x0000000100003690 (KCObjcBuild`-[JSPerson setName:]) }
可以看到,扩展
中的方向现在已经加载了已知ro
中的方法是编译期
就确定的,所以也验证了扩展
的方法是在编译期
添加到本类
的。
小结
类的扩展 在编译期
会作为类的一部分,和类一起编译进来
类的扩展只是声明
,依赖于本类
的实现。
分类的关联对象 我们知道分类
正常是不能添加属性
的,但是通过关联对象
可以,其实现通过两个方法
通过objc_setAssociatedObject
方法设置值。
通过objc_getAssociatedObject
方法取值。
下面我们分别探索。
objc_setAssociatedObject流程 objc_setAssociatedObject
有四个参数:
参数1:要关联的对象
参数2:表示符,方便查找识别
参数3:value值
参数4:属性的策略
,我们定义属性经常用到的如nonatomic
、strong
、weak
。
首先定义JSPerson
的分类
,定义一个属性cateegory_name
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @interface JSPerson (JSCategory) @property (nonatomic, copy) NSString *cateegory_name; @end @implementation JSPerson (JSCategory) - (void)setCateegory_name:(NSString *)category_name{ objc_setAssociatedObject(self, "category_name", category_name, OBJC_ASSOCIATION_COPY_NONATOMIC); } - (NSString *)category_name{ return objc_getAssociatedObject(self, "category_name"); } @end
在main
函数赋值属性的地方添加断点,根据调用情况
定位到objc_setAssociatedObject
方法
调用的是_object_set_associative_reference
方法,我们继续跟进查看源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 void _object_set_associative_reference(id object, const void *key, id value, uintptr_t policy) { 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}; association.acquireValue(); bool isFirstAssociation = false ; { AssociationsManager manager; AssociationsHashMap &associations (manager.get ()) ; if (value) { auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{}); if (refs_result.second) { isFirstAssociation = true ; } 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); } } } } } if (isFirstAssociation) object->setHasAssociatedObjects(); association.releaseHeldValue(); }
通过源码我们看到大体的流程为:
创建一个AssociationsManager
管理类
获取静态哈希表
:associations
判断关联值value
是否为空
如果为空就走:插入空值
流程。
如果不为空继续下一步
通过try_emplace
方法,创建一个空的 ObjectAssociationMap
去取查询的键值对
如果发现没有
key
就插入一个 空的 BucketT
进去并返回true
通过setHasAssociatedObjects
方法标记对象存在关联对象
用当前 policy 和 value
组成了一个 ObjcAssociation
替换原来 BucketT
中的值
标记一下 ObjectAssociationMap
的第一次
为 false
源码调试 对流程有了大概的认识,我们开始断点调试
if (value) 之前变量的值
通过lldb
我们打印出了disguised
、association
、manager
、associations
、value
的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 (lldb) p disguised (DisguisedPtr<objc_object>) $0 = (value = 18446744069393517536 ) (lldb) p association (objc::ObjcAssociation) $1 = { _policy = 3 _value = 0x0000000100004080 "哈哈哈" } (lldb) p manager (objc::AssociationsManager) $2 = {} (lldb) p associations (objc::AssociationsHashMap) $3 = { Buckets = nil NumEntries = 0 NumTombstones = 0 NumBuckets = 0 } (lldb) p value (__NSCFConstantString *) $4 = 0x0000000100004080 "哈哈哈"
value
不为空流程上面我们看到value
值不为空,我们进入if
语句继续调试。
1 2 3 4 5 6 7 8 (lldb) p refs_result (std ::pair<objc::DenseMapIterator<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >, objc::DenseMapValueInfo<objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >, objc::DenseMapInfo<DisguisedPtr<objc_object> >, objc::detail::DenseMapPair<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >, false >, bool >) $5 = { first = { Ptr = 0x00000001012102a0 End = 0x0000000101210300 } second = true }
看到refs_result
的数据结构看起来比较复杂,但是值比较简单,有两个属性first
、second
。其中first
的值为:
1 2 3 4 (lldb) p $5.f irst.Ptr (objc::DenseMapIterator<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >, objc::DenseMapValueInfo<objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >, objc::DenseMapInfo<DisguisedPtr<objc_object> >, objc::detail::DenseMapPair<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >, false >::pointer) $6 = 0x00000001012102a0 (lldb) p $5.f irst.End (objc::DenseMapIterator<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >, objc::DenseMapValueInfo<objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >, objc::DenseMapInfo<DisguisedPtr<objc_object> >, objc::detail::DenseMapPair<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >, false >::pointer) $7 = 0x0000000101210300
second
值为true
,所以会执行isFirstAssociation = true
。
try_emplace
方法,associations
调用了try_emplace
方法,我们看一下他的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 template <typename ... Ts>std::pair<iterator, bool> try_emplace(const KeyT &Key, Ts &&... Args) { BucketT *TheBucket; if (LookupBucketFor(Key, TheBucket)) return std ::make_pair( makeIterator(TheBucket, getBucketsEnd(), true ), false ); TheBucket = InsertIntoBucket(TheBucket, Key, std ::forward<Ts>(Args)...); return std ::make_pair( makeIterator(TheBucket, getBucketsEnd(), true ), true ); }
有两个返回,都是通过std::make_pair
生成相应的键值对。
通过LookupBucketFor
方法查找桶
,如果map中已经存在
,则直接返回
,其中make_pair
的第二个参数bool值为false
如果没有找到
,则通过InsertIntoBucket
插入map,其中make_pair
的第二个参数bool值为true
我们断点进来使用lldb
调试
p TheBucket
1 2 3 4 5 6 7 8 9 10 11 12 13 14 (lldb) p TheBucket (objc::detail::DenseMapPair<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > > *) $1 = 0x0000000101c04200 (lldb) p *$1 (objc::detail::DenseMapPair<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >) $2 = { std ::__1::pair<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > > = { first = (value = 18446744069384153152 ) second = { Buckets = nil NumEntries = 0 NumTombstones = 0 NumBuckets = 0 } } }
看到TheBucket
的类型与 refs_result
中属性的类型是一致的。
LookupBucketFor
方法
我们进入LookupBucketFor
源码发现有两个实现,它们的区别是FoundBucket
的参数类型第一个实现多const
修饰。
我们通过断点调试,发现调用的是第2个实现,第2个方法内部调用了第1个实现。我们先看第1个实现源码,注释中有流程说明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 template <typename LookupKeyT> bool LookupBucketFor (const LookupKeyT &Val, const BucketT *&FoundBucket) const { const BucketT *BucketsPtr = getBuckets(); const unsigned NumBuckets = getNumBuckets(); if (NumBuckets == 0 ) { FoundBucket = nullptr ; return false ; } const BucketT *FoundTombstone = nullptr ; const KeyT EmptyKey = getEmptyKey(); const KeyT TombstoneKey = getTombstoneKey(); assert(!KeyInfoT::isEqual(Val, EmptyKey) && !KeyInfoT::isEqual(Val, TombstoneKey) && "Empty/Tombstone value shouldn't be inserted into map!" ); unsigned BucketNo = getHashValue(Val) & (NumBuckets-1 ); unsigned ProbeAmt = 1 ; while (true ) { const BucketT *ThisBucket = BucketsPtr + BucketNo; if (LLVM_LIKELY(KeyInfoT::isEqual(Val, ThisBucket->getFirst()))) { FoundBucket = ThisBucket; return true ; } if (LLVM_LIKELY(KeyInfoT::isEqual(ThisBucket->getFirst(), EmptyKey))) { FoundBucket = FoundTombstone ? FoundTombstone : ThisBucket; return false ; } if (KeyInfoT::isEqual(ThisBucket->getFirst(), TombstoneKey) && !FoundTombstone) FoundTombstone = ThisBucket; if (ValueInfoT::isPurgeable(ThisBucket->getSecond()) && !FoundTombstone) FoundTombstone = ThisBucket; if (ProbeAmt > NumBuckets) { FatalCorruptHashTables(BucketsPtr, NumBuckets); } BucketNo += ProbeAmt++; BucketNo &= (NumBuckets-1 ); } }
第2个LookupBucketFor
的实现
1 2 3 4 5 6 7 bool LookupBucketFor (const LookupKeyT &Val, BucketT *&FoundBucket) { const BucketT *ConstFoundBucket; bool Result = const_cast <const DenseMapBase *>(this ) ->LookupBucketFor(Val, ConstFoundBucket); FoundBucket = const_cast <BucketT *>(ConstFoundBucket); return Result; }
继续走value
为true
的流程
后面还会执行try_emplace
方法,我们在执行之前查看一下refs
的值
1 2 3 4 5 6 7 (lldb) p refs (objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >) $3 = { Buckets = nil NumEntries = 0 NumTombstones = 0 NumBuckets = 0 }
try_emplace
方法之后refs
的值
1 2 3 4 5 6 7 (lldb) p refs (objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >) $4 = { Buckets = 0x0000000100711390 NumEntries = 1 NumTombstones = 0 NumBuckets = 4 }
第一次执行try_emplace
插入的是一个空桶,还没有值,第二次执行第一次执行try_emplace
才插入值,即往空桶中插入ObjectAssociationMap(value,policy)
,返回true。
此时result.second
为true
,此时属性的value
就关联上了。
关联对象结构 关联对象的设置图如下:
属性设计的哈希表结构如下:
map中有很多的关联对象map,类型是ObjectAssociationMap
,其中key为DisguisedPtr<objc_object>
,例如JSPerson
会对应一个ObjectAssociationMap
,JSTeacher
也会对应一个ObjectAssociationMap
。
ObjectAssociationMap
哈希表中有很多key-value
键值对,其中key
的类型为const void *
,其实这个key
从底层这个方法_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
的参数就可以看出,key
是我们关联属性时设置的字符串
,value
的类型为ObjcAssociation
value
为空流程这个过程其实就是else
流程,也就是我们对value
设置为nil
的流程,主要就是移除关联。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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); } } } }
根据 DisguisedPtr 找到 AssociationsHashMap 中的 iterator 迭代查询器
清理迭代器
实际上如果插入空置 相当于清除
objc_getAssociatedObject流程 在main
方法中添加一个取值的语句 1 2 3 4 5 6 7 8 9 int main(int argc, const char * argv[]) { @autoreleasepool { JSPerson *person = [[JSPerson alloc] init]; person.category_name = @"哈哈哈"; NSString *name = person.category_name; NSLog(@"done"); } return 0; }
objc_getAssociatedObject
源码实现1 2 3 4 5 id objc_getAssociatedObject(id object, const void *key) { return _object_get_associative_reference(object, key); }
调用_object_get_associative_reference
函数。
_object_get_associative_reference
源码1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 id _object_get_associative_reference(id object, const void *key) { ObjcAssociation association{}; { AssociationsManager manager; AssociationsHashMap &associations (manager.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(); }
看源码分析主要分为以下几步
创建一个 AssociationsManager
管理类
获取静态哈希表:AssociationsHashMap
通过find
方法根据 DisguisedPtr
找到 AssociationsHashMap
中的 iterator
迭代查询器
如果这个迭代查询器不是最后一个 继续获取 : ObjectAssociationMap (policy和value)
通过find
方法找到ObjectAssociationMap
的迭代查询器获取一个经过属性修饰符修饰的value
返回 value
查找方法find
1 2 3 4 5 6 iterator find (const_arg_type_t <KeyT> Val) { BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) return makeIterator(TheBucket, getBucketsEnd(), true ); return end (); }
根据关联对象迭代查找AssociationsHashMap
,也就是buckets
通过源码看取值流程 我们直接断点到_object_get_associative_reference
函数
执行p i
和p i->second
:
1 2 3 4 5 6 7 8 9 10 11 12 (lldb) p i (objc::DenseMapBase<objc::DenseMap<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >, objc::DenseMapValueInfo<objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >, objc::DenseMapInfo<DisguisedPtr<objc_object> >, objc::detail::DenseMapPair<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > > >, DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >, objc::DenseMapValueInfo<objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >, objc::DenseMapInfo<DisguisedPtr<objc_object> >, objc::detail::DenseMapPair<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > > >::iterator) $0 = { Ptr = 0x0000000100631d60 End = 0x0000000100631d80 } (lldb) p i->second (objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >) $1 = { Buckets = 0x0000000100631d80 NumEntries = 1 NumTombstones = 0 NumBuckets = 4 }
再次执行find
方法,在调用find
方法之前,我们先打印j
,此时value
为nil
。
1 2 3 4 5 6 7 8 9 10 11 (lldb) p j (objc::DenseMapBase<objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >, const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >::iterator) $2 = { Ptr = 0x00007ffeefbff400 End = 0x00000001002e70db } (lldb) p j->second (objc::ObjcAssociation) $3 = { _policy = 4294980472 _value = nil } (lldb)
执行find
方法之后再次打印,发现value
已经有值,也就是取到了关联对象。
1 2 3 4 5 6 7 8 9 10 (lldb) p j (objc::DenseMapBase<objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >, const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >::iterator) $4 = { Ptr = 0x0000000100631d80 End = 0x0000000100631de0 } (lldb) p j->second (objc::ObjcAssociation) $5 = { _policy = 3 _value = 0x0000000100004080 "哈哈哈" }
总结 本篇主要探索了扩展
和关联对象
,其中类的扩展
在编译期
会作为类的一部分,和类一起编译进来。
关联对象设置流程为:
创建一个AssociationsManager
管理类
获取静态哈希表
:associations
判断关联值value
是否为空
如果为空就走:插入空值
流程。
如果不为空继续下一步
通过try_emplace
方法,创建一个空的 ObjectAssociationMap
去取查询的键值对
如果发现没有
key
就插入一个 空的 BucketT
进去并返回true
通过setHasAssociatedObjects
方法标记对象存在关联对象
用当前 policy 和 value
组成了一个 ObjcAssociation
替换原来 BucketT
中的值
标记一下 ObjectAssociationMap
的第一次
为 false
关联对象取值的流程为:
创建一个 AssociationsManager
管理类
获取静态哈希表:AssociationsHashMap
通过find
方法根据 DisguisedPtr
找到 AssociationsHashMap
中的 iterator
迭代查询器
如果这个迭代查询器不是最后一个 继续获取 : ObjectAssociationMap (policy和value)
通过find
方法找到ObjectAssociationMap
的迭代查询器获取一个经过属性修饰符修饰的value
返回 value