前面几篇我们探索了类的加载过程,本篇我们研究类相关的两个点:类的扩展和关联对象。
类扩展
clang编译
@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 -rewrite-objc main.m -o main.cpp
发现
扩展里声明的属性和方法编译后和类中的在一起,作为类的一部分,也就是说扩展中的属性和方法在编译期就添加到本类中了。
通过源码探索运行时
定义一个JSPerson类和扩展,本类中实现扩展中声明的方法。
@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
@interface JSPerson ()
- (void)ext_instanceMethod;
+ (void)ext_classMethod;
@end
注意JSPerson类实现了load方法,目的是让其非懒加载。根据我们的经验,我们在realizeClassWithoutSwift添加断点调试
通过
lldb打印ro中的方法列表
(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.baseMethods()
(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:
@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方法,我们继续跟进查看源码:
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));
///将object封装一下 类型为DisguisedPtr
DisguisedPtr<objc_object> disguised{(objc_object *)object};
///包装policy value
ObjcAssociation association{policy, value};
// retain the new value (if any) outside the lock.
//根据策略类型(strong、weak等)进行处理
association.acquireValue();
bool isFirstAssociation = false;
{
//初始化manager变量,相当于自动调用AssociationsManager的构造函数进行初始化
AssociationsManager manager;
///一个HashMap
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 */
isFirstAssociation = true;
}
/* establish or replace the association 建立或者替换关联*/
auto &refs = refs_result.first->second;///得到一个空的桶,找到引用对象类型,即第一个元素的second值
auto result = refs.try_emplace(key, std::move(association));//查找当前的key是否有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);
}
}
}
}
}
// Call setHasAssociatedObjects outside the lock, since this
// will call the object's _noteAssociatedObjects method if it
// has one, and this may trigger +initialize which might do
// arbitrary stuff, including setting more associated objects.
if (isFirstAssociation)
object->setHasAssociatedObjects();
// release the old value (outside of the lock).释放老的关联值
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的值
(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语句继续调试。
-
p refs_result(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的值为:
(lldb) p $5.first.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.first.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方法,我们看一下他的源码
template <typename... Ts>
std::pair<iterator, bool> try_emplace(const KeyT &Key, Ts &&... Args) {
BucketT *TheBucket;
///根据key找桶
if (LookupBucketFor(Key, TheBucket))
///如果桶存在 则返回
return std::make_pair(
makeIterator(TheBucket, getBucketsEnd(), true),
false); // Already in map.
// Otherwise, insert the new element.
///如果不存在则插入桶 并返回
TheBucket = InsertIntoBucket(TheBucket, Key, std::forward<Ts>(Args)...);
return std::make_pair(
makeIterator(TheBucket, getBucketsEnd(), true),
true);
}
- 通过
LookupBucketFor方法查找桶,如果map中已经存在,则直接返回,其中make_pair的第二个参数bool值为false - 如果没
有找到,则通过InsertIntoBucket插入map,其中make_pair的第二个参数bool值为true我们断点进来使用lldb调试
`p TheBucket
(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个实现源码,注释中有流程说明。
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;
}
// FoundTombstone - Keep track of whether we find a tombstone while probing.
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!");
///通过哈希函数得到BucketNo
unsigned BucketNo = getHashValue(Val) & (NumBuckets-1);
unsigned ProbeAmt = 1;
while (true) {//与catche_t查找imp类似,通过哈希查找
const BucketT *ThisBucket = BucketsPtr + BucketNo;
// Found Val's bucket? If so, return it. 如果找到直接返回 true
if (LLVM_LIKELY(KeyInfoT::isEqual(Val, ThisBucket->getFirst()))) {
FoundBucket = ThisBucket;
return true;
}
// If we found an empty bucket, the key doesn't exist in the set.
// Insert it and return the default value.
//如果是一个空桶 说明key不在集合中,将key插入 返回false
if (LLVM_LIKELY(KeyInfoT::isEqual(ThisBucket->getFirst(), EmptyKey))) {
// If we've already seen a tombstone while probing, fill it in instead
// of the empty bucket we eventually probed to.
FoundBucket = FoundTombstone ? FoundTombstone : ThisBucket;
return false;
}
// If this is a tombstone, remember it. If Val ends up not in the map, we
// prefer to return it than something that would require more probing.
// Ditto for zero values.
// 以上条件都不满足 BucketNo调整进行平移、再哈希继续查找
if (KeyInfoT::isEqual(ThisBucket->getFirst(), TombstoneKey) &&
!FoundTombstone)
FoundTombstone = ThisBucket; // Remember the first tombstone found.
if (ValueInfoT::isPurgeable(ThisBucket->getSecond()) && !FoundTombstone)
FoundTombstone = ThisBucket;
// Otherwise, it's a hash collision or a tombstone, continue quadratic
// probing.
if (ProbeAmt > NumBuckets) {
FatalCorruptHashTables(BucketsPtr, NumBuckets);
}
BucketNo += ProbeAmt++;
BucketNo &= (NumBuckets-1);
}
}
第2个LookupBucketFor的实现
-
bool LookupBucketFor(const LookupKeyT &Val, BucketT *&FoundBucket) { const BucketT *ConstFoundBucket;//空的桶 bool Result = const_cast<const DenseMapBase *>(this) ->LookupBucketFor(Val, ConstFoundBucket);//调用第一个LookupBucketFor方法查找 FoundBucket = const_cast<BucketT *>(ConstFoundBucket);//如果找到复制给第二个参数,因为第二个参数是引用类型会直接让调用的地方获取到值。也就是try_emplace方法的TheBucket return Result; } -
继续走
value为true的流程后面还会执行
try_emplace方法,我们在执行之前查看一下refs的值(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的值(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的流程,主要就是移除关联。
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方法中添加一个取值的语句
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源码实现
id
objc_getAssociatedObject(id object, const void *key)
{
return _object_get_associative_reference(object, key);
}
调用_object_get_associative_reference函数。
_object_get_associative_reference源码
id
_object_get_associative_reference(id object, const void *key)
{
ObjcAssociation association{};
{
///创建AssociationsManager管理类
AssociationsManager manager;
///获取静态哈希表
AssociationsHashMap &associations(manager.get());
/////找到迭代器,即获取buckets
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
if (i != associations.end()) {//如果这个迭代查询器不是最后一个 继续获取
//找到ObjectAssociationMap的迭代查询器获取一个经过属性修饰符修饰的value
ObjectAssociationMap &refs = i->second;
//根据key查找ObjectAssociationMap,即获取bucket
ObjectAssociationMap::iterator j = refs.find(key);
if (j != refs.end()) {
//获取ObjcAssociation
association = j->second;
association.retainReturnedValue();
}
}
}
///返回值
return association.autoreleaseReturnedValue();
}
看源码分析主要分为以下几步
-
创建一个
AssociationsManager管理类 -
获取静态哈希表:
AssociationsHashMap -
通过
find方法根据DisguisedPtr找到AssociationsHashMap中的iterator迭代查询器 -
如果这个迭代查询器不是最后一个 继续获取 :
ObjectAssociationMap (policy和value) -
通过
find方法找到ObjectAssociationMap的迭代查询器获取一个经过属性修饰符修饰的value -
返回
value查找方法
finditerator find(const_arg_type_t<KeyT> Val) { BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) return makeIterator(TheBucket, getBucketsEnd(), true); return end(); }通过源码看取值流程
我们直接断点到
_object_get_associative_reference函数
执行
p i和p i->second:
```
(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。
(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已经有值,也就是取到了关联对象。
(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
- 创建一个