1. 类扩展 VS 分类

1.1 类扩展(Extension

  • 特殊的分类,也称作匿名分类
  • 可以给类添加成员属性,但是为私有变量
  • 可以给类添加方法,也是私有方法

1.2 分类(Category

  • 专门用来给类添加新的方法
  • 不能给类添加成员属性,添加后无法读取(可通过Runtime给分类添加属性)
  • 分类中使用@property定义变量,只会生成变量的getter/setter方法的声明,不能生成方法的实现和下划线开头的成员变量

2. 类扩展的分析

2.1 cpp文件探索

main函数中,写入LGPerson的类扩展

  1. #import <Foundation/Foundation.h>
  2. int main(int argc, const char * argv[]) {
  3. @autoreleasepool {
  4. NSLog(@"Hello, World!");
  5. }
  6. return 0;
  7. }
  8. @interface LGPerson : NSObject
  9. @property (nonatomic, copy) NSString *lgName;
  10. - (void)say666;
  11. @end
  12. @interface LGPerson ()
  13. @property (nonatomic, copy) NSString *ext_name;
  14. - (void)ext_sayNB;
  15. @end
  16. @implementation LGPerson
  17. - (void)say666{
  18. NSLog(@"%@ - %s",self , __func__);
  19. }
  20. - (void)ext_sayNB{
  21. NSLog(@"%@ - %s",self , __func__);
  22. }
  23. @end
  • 类扩展的书写位置:声明之后,实现之前

生成cpp文件

  1. clang -rewrite-objc main.m -o main.cpp

对于类扩展中声明的属性,同样会生成下划线开头的成员变量

  1. extern "C" unsigned long OBJC_IVAR_$_LGPerson$_lgName;
  2. extern "C" unsigned long OBJC_IVAR_$_LGPerson$_ext_name;
  3. struct LGPerson_IMPL {
  4. struct NSObject_IMPL NSObject_IVARS;
  5. NSString *_lgName;
  6. NSString *_ext_name;
  7. };

生成属性的getter/setter方法

  1. static NSString * _I_LGPerson_lgName(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_lgName)); }
  2. extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
  3. static void _I_LGPerson_setLgName_(LGPerson * self, SEL _cmd, NSString *lgName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _lgName), (id)lgName, 0, 1); }
  4. static NSString * _I_LGPerson_ext_name(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_ext_name)); }
  5. static void _I_LGPerson_setExt_name_(LGPerson * self, SEL _cmd, NSString *ext_name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _ext_name), (id)ext_name, 0, 1); }

找到LGPerson的成员变量列表

  1. extern "C" unsigned long int OBJC_IVAR_$_LGPerson$_lgName __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct LGPerson, _lgName);
  2. extern "C" unsigned long int OBJC_IVAR_$_LGPerson$_ext_name __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct LGPerson, _ext_name);
  3. static struct /*_ivar_list_t*/ {
  4. unsigned int entsize; // sizeof(struct _prop_t)
  5. unsigned int count;
  6. struct _ivar_t ivar_list[2];
  7. } _OBJC_$_INSTANCE_VARIABLES_LGPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
  8. sizeof(_ivar_t),
  9. 2,
  10. {{(unsigned long int *)&OBJC_IVAR_$_LGPerson$_lgName, "_lgName", "@\"NSString\"", 3, 8},
  11. {(unsigned long int *)&OBJC_IVAR_$_LGPerson$_ext_name, "_ext_name", "@\"NSString\"", 3, 8}}
  12. };

找到LGPerson的方法列表

  1. static struct /*_method_list_t*/ {
  2. unsigned int entsize; // sizeof(struct _objc_method)
  3. unsigned int method_count;
  4. struct _objc_method method_list[10];
  5. } _OBJC_$_INSTANCE_METHODS_LGPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
  6. sizeof(_objc_method),
  7. 10,
  8. {{(struct objc_selector *)"say666", "v16@0:8", (void *)_I_LGPerson_say666},
  9. {(struct objc_selector *)"ext_sayNB", "v16@0:8", (void *)_I_LGPerson_ext_sayNB},
  10. {(struct objc_selector *)"lgName", "@16@0:8", (void *)_I_LGPerson_lgName},
  11. {(struct objc_selector *)"setLgName:", "v24@0:8@16", (void *)_I_LGPerson_setLgName_},
  12. {(struct objc_selector *)"ext_name", "@16@0:8", (void *)_I_LGPerson_ext_name},
  13. {(struct objc_selector *)"setExt_name:", "v24@0:8@16", (void *)_I_LGPerson_setExt_name_},
  14. {(struct objc_selector *)"lgName", "@16@0:8", (void *)_I_LGPerson_lgName},
  15. {(struct objc_selector *)"setLgName:", "v24@0:8@16", (void *)_I_LGPerson_setLgName_},
  16. {(struct objc_selector *)"ext_name", "@16@0:8", (void *)_I_LGPerson_ext_name},
  17. {(struct objc_selector *)"setExt_name:", "v24@0:8@16", (void *)_I_LGPerson_setExt_name_}}
  18. };

类扩展和分类的明显区别,不会生成额外的结构体存储类扩展的信息、类扩展中的属性和方法,和LGPerson主类的属性和方法融为一体

2.2 objc源码探索

在项目中,创建LGPerson+Ext.h类扩展

  1. #import "LGPerson.h"
  2. @interface LGPerson ()
  3. @property (nonatomic, copy) NSString *ext_name;
  4. - (void)ext_say666;
  5. @end

LGPerson中,实现ext_say666方法

  1. #import "LGPerson.h"
  2. #import "LGPerson+Ext.h"
  3. @implementation LGPerson
  4. + (void)load{
  5. NSLog(@"%@ - %s",self , __func__);
  6. }
  7. - (void)say1{
  8. NSLog(@"%@ : %s",self,__func__);
  9. }
  10. - (void)ext_say666{
  11. NSLog(@"%@ : %s",self,__func__);
  12. }
  13. @end

主类执行流程没有任何变化,在realizeClassWithoutSwift函数中,读取ro方法列表中,同时也包含类扩展中声明的方法
image.png

所以类扩展并不会影响主类的加载流程,在编译时,类扩展中的属性和方法和主类融为一体

创建的类扩展只有·h文件,所以只做声明,实现需要依赖于当前的主类

3. 关联对象

分类中,不能直接使用属性的getter/setter方法,只能使用关联对象的方式实现成员变量的效果

关联对象的两个核心方法,值的存储和读取

值的存储

  1. void
  2. objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
  • object:被关联者
  • key:标识符
  • value:值
  • policy:关联策略

值的读取

  1. void objc_removeAssociatedObjects(id object)
  • object:被关联者
  • key:标识符

关联策略

  1. typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
  2. OBJC_ASSOCIATION_ASSIGN = 0, /**< Specifies a weak reference to the associated object. */
  3. OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
  4. * The association is not made atomically. */
  5. OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied.
  6. * The association is not made atomically. */
  7. OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object.
  8. * The association is made atomically. */
  9. OBJC_ASSOCIATION_COPY = 01403 /**< Specifies that the associated object is copied.
  10. * The association is made atomically. */
  11. };
  • nonatomicatomicassignretaincopy等修饰

3.1 objc_setAssociatedObject & objc_getAssociatedObject

  1. void
  2. objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
  3. {
  4. _object_set_associative_reference(object, key, value, policy);
  5. }
  6. id
  7. objc_getAssociatedObject(id object, const void *key)
  8. {
  9. return _object_get_associative_reference(object, key);
  10. }
  • objc_setAssociatedObjectobjc_getAssociatedObject函数为中间层层代码,目的是对业务层进行隔离。因为底层API根据不同版本会发生变化,但中间层代码是保持不变的,所以开发者不用担心因底层API变化导致程序的修改

3.2 _object_set_associative_reference

  1. void
  2. _object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
  3. {
  4. // This code used to work when nil was passed for object and key. Some code
  5. // probably relies on that to not crash. Check and handle it explicitly.
  6. // rdar://problem/44094390
  7. if (!object && !value) return;
  8. if (object->getIsa()->forbidsAssociatedObjects())
  9. _objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
  10. //将不同类型的object,保存成统一结构
  11. DisguisedPtr<objc_object> disguised{(objc_object *)object};
  12. //将关联策略和值,以面向对象的方式进行存储
  13. ObjcAssociation association{policy, value};
  14. // retain the new value (if any) outside the lock.
  15. //根据不同的关联策略进行处理
  16. association.acquireValue();
  17. bool isFirstAssociation = false;
  18. {
  19. //调用构造函数,加锁。函数作用域结束AssociationsManager释放,调用析构函数,解锁
  20. AssociationsManager manager;
  21. //获取唯⼀的全局静态哈希Map
  22. //验证方式:get函数中调用_mapStorage.get(),而_mapStorage为static修饰
  23. AssociationsHashMap &associations(manager.get());
  24. if (value) {
  25. //被关联者和ObjectAssociationMap进行关联
  26. auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
  27. if (refs_result.second) {
  28. /* it's the first association we make */
  29. //首次关联,打标记
  30. isFirstAssociation = true;
  31. }
  32. /* establish or replace the association */
  33. //建立或替换关联
  34. auto &refs = refs_result.first->second;
  35. auto result = refs.try_emplace(key, std::move(association));
  36. if (!result.second) {
  37. //如果存在旧值,替换为新值
  38. association.swap(result.first->second);
  39. }
  40. } else {
  41. //value不存在,移除关联
  42. //通过关联对象寻址Iterator
  43. auto refs_it = associations.find(disguised);
  44. if (refs_it != associations.end()) {
  45. //获取ObjectAssociationMap
  46. auto &refs = refs_it->second;
  47. //通过key寻址Iterator
  48. auto it = refs.find(key);
  49. if (it != refs.end()) {
  50. //替换ObjcAssociation,新值替换旧值
  51. association.swap(it->second);
  52. //移除旧值
  53. refs.erase(it);
  54. if (refs.size() == 0) {
  55. //如果关联对象关联的Iterator为空,直接移除
  56. associations.erase(refs_it);
  57. }
  58. }
  59. }
  60. }
  61. }
  62. // Call setHasAssociatedObjects outside the lock, since this
  63. // will call the object's _noteAssociatedObjects method if it
  64. // has one, and this may trigger +initialize which might do
  65. // arbitrary stuff, including setting more associated objects.
  66. //在nonpointerIsa中标记
  67. if (isFirstAssociation)
  68. object->setHasAssociatedObjects();
  69. // release the old value (outside of the lock).
  70. //释放旧值
  71. association.releaseHeldValue();
  72. }

【步骤一】

  • 将不同类型的object,保存成统一的DisguisedPtr<objc_object>结构

  • 将关联策略和值,以面向对象的方式进行存储ObjcAssociation类中

  • 根据不同的关联策略进行处理

【步骤二】

  • 调用构造函数,加锁。函数作用域结束AssociationsManager释放,调用析构函数,解锁

  • AssociationsHashMap为单例,在内存中只有一份

【步骤三】

  • 如果value存在,进行关联

    • 调用AssociationsHashMaptry_emplace函数,将objectObjectAssociationMap关联

    • 返回结果refs_result,如果是首次关联,打标记

    • refs_result中找到object关联的Bucket

    • 调用Buckettry_emplace函数,将key关联ObjcAssociation

    • 如果存在旧值,替换为新值

  • 如果传入的value不存在,移除关联

    • 通过关联对象寻址Iterator

    • 获取ObjectAssociationMap

    • 替换ObjcAssociation,新值替换旧值

    • 移除旧值

    • 如果关联对象关联的Iterator为空,直接移除

【步骤四】

  • nonpointerIsa中标记,当对象销毁时,关联对象也要跟随销毁

  • 释放旧值

3.2.1 AssociationsHashMap单例

  1. class AssociationsManager {
  2. using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>;
  3. static Storage _mapStorage;
  4. public:
  5. AssociationsManager() { AssociationsManagerLock.lock(); }
  6. ~AssociationsManager() { AssociationsManagerLock.unlock(); }
  7. AssociationsHashMap &get() {
  8. return _mapStorage.get();
  9. }
  10. static void init() {
  11. _mapStorage.init();
  12. }
  13. };
  • AssociationsManagerget函数,调用_mapStorageget函数
  • _mapStorage:使用static修饰,全局静态对象
  • AssociationsManager():构造函数,加锁
  • ~AssociationsManager():析构函数,解锁
  • 加锁、解锁操作,保证对象的安全性,防止冲突

验证AssociationsManagerAssociationsHashMap的单例模式,将AssociationsManager中的构造函数和析构函数注释,避免锁递归报错

  1. void test(){
  2. AssociationsManager manager1;
  3. AssociationsHashMap &associations1(manager1.get());
  4. printf("AssociationsManager1:%p\n", &manager1);
  5. printf("AssociationsHashMap1:%p\n", &associations1);
  6. AssociationsManager manager2;
  7. AssociationsHashMap &associations2(manager2.get());
  8. printf("AssociationsManager2:%p\n", &manager2);
  9. printf("AssociationsHashMap2:%p\n", &associations2);
  10. AssociationsManager manager3;
  11. AssociationsHashMap &associations3(manager3.get());
  12. printf("AssociationsManager3:%p\n", &manager3);
  13. printf("AssociationsHashMap3:%p\n", &associations3);
  14. }
  15. -------------------------
  16. AssociationsManager10x7ffeefbff258
  17. AssociationsHashMap10x100370188
  18. AssociationsManager20x7ffeefbff248
  19. AssociationsHashMap20x100370188
  20. AssociationsManager30x7ffeefbff238
  21. AssociationsHashMap30x100370188
  • AssociationsManager不是单例对象,每次创建的内存地址都不一样
  • AssociationsHashMap为单例对象,内存地址一致

3.2.2 AssociationsManager的初始化

AssociationsManagerinit函数使用static修饰,表示类对象函数,找到它的初始化时机

_objc_associations_init函数

  1. void
  2. _objc_associations_init()
  3. {
  4. AssociationsManager::init();
  5. }

arr_init函数

  1. void arr_init(void)
  2. {
  3. AutoreleasePoolPage::init();
  4. SideTablesMap.init();
  5. _objc_associations_init();
  6. }

map_images_nolock函数

  1. void
  2. map_images_nolock(unsigned mhCount, const char * const mhPaths[],
  3. const struct mach_header * const mhdrs[])
  4. {
  5. ...
  6. if (firstTime) {
  7. sel_init(selrefCount);
  8. arr_init();
  9. ...
  10. }
  11. ...
  12. }

AssociationsManager初始化流程:map_imagesmap_images_nolockarr_init_objc_associations_init

也就是在此时,系统创建了AssociationsHashMap关联对象表。和它一起被创建的全局系统表,还有AutoreleasePoolPage自动释放池,以及SideTablesMap散列表

SideTablesMap散列表中包含两张子表,分别是RefcountMap引用计数表,还有weak_table_t弱引用表

3.2.3 AssociationsHashMap结构

image.png

AssociationsHashMap为双层HashMap结构

  1. typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;
  • DisguisedPtr<objc_object>为被关联者,它所对应的值还是一张HashMap

image.png

ObjectAssociationMapHashMap结构

  1. typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap;
  • const void *为标识号,它所对应的值为ObjcAssociation

image.png

ObjcAssociationclass结构

  1. class ObjcAssociation {
  2. uintptr_t _policy;
  3. id _value;
  4. ...
  5. }
  • _policy:关联策略
  • _value:值

3.2.4 try_emplace函数

  1. template <typename... Ts>
  2. std::pair<iterator, bool> try_emplace(const KeyT &Key, Ts &&... Args) {
  3. BucketT *TheBucket;
  4. //查看Bucket在map中是否存在,存在将Bucket返回
  5. if (LookupBucketFor(Key, TheBucket))
  6. return std::make_pair(
  7. makeIterator(TheBucket, getBucketsEnd(), true),
  8. false); // Already in map.
  9. // Otherwise, insert the new element.
  10. //不存在,插入Bucket并返回
  11. TheBucket = InsertIntoBucket(TheBucket, Key, std::forward<Ts>(Args)...);
  12. return std::make_pair(
  13. makeIterator(TheBucket, getBucketsEnd(), true),
  14. true);
  15. }
  • 通过std::make_pair生成相应的键值对
  • LookupBucketFor函数,查看Bucketmap中是否存在,存在将Bucket返回
  • 不存在,调用InsertIntoBucket函数,插入Bucket并返回

3.2.5 LookupBucketFor函数

image.png

  • 两个函数的区别在于Bucket参数的const修饰
  • 作用:为value查找适当的Bucket,并将其返回

LookupBucketFor函数一,Bucket使用const修饰

  1. template<typename LookupKeyT>
  2. bool LookupBucketFor(const LookupKeyT &Val,
  3. const BucketT *&FoundBucket) const {
  4. const BucketT *BucketsPtr = getBuckets();
  5. const unsigned NumBuckets = getNumBuckets();
  6. if (NumBuckets == 0) {
  7. FoundBucket = nullptr;
  8. return false;
  9. }
  10. // FoundTombstone - Keep track of whether we find a tombstone while probing.
  11. const BucketT *FoundTombstone = nullptr;
  12. const KeyT EmptyKey = getEmptyKey();
  13. const KeyT TombstoneKey = getTombstoneKey();
  14. assert(!KeyInfoT::isEqual(Val, EmptyKey) &&
  15. !KeyInfoT::isEqual(Val, TombstoneKey) &&
  16. "Empty/Tombstone value shouldn't be inserted into map!");
  17. //哈希函数,计算下标
  18. unsigned BucketNo = getHashValue(Val) & (NumBuckets-1);
  19. unsigned ProbeAmt = 1;
  20. while (true) {
  21. //内存平移,获取Bucket
  22. const BucketT *ThisBucket = BucketsPtr + BucketNo;
  23. // Found Val's bucket? If so, return it.
  24. //找到Bucket,直接返回
  25. if (LLVM_LIKELY(KeyInfoT::isEqual(Val, ThisBucket->getFirst()))) {
  26. FoundBucket = ThisBucket;
  27. return true;
  28. }
  29. // If we found an empty bucket, the key doesn't exist in the set.
  30. // Insert it and return the default value.
  31. //Bucket为空,返回默认值
  32. if (LLVM_LIKELY(KeyInfoT::isEqual(ThisBucket->getFirst(), EmptyKey))) {
  33. // If we've already seen a tombstone while probing, fill it in instead
  34. // of the empty bucket we eventually probed to.
  35. FoundBucket = FoundTombstone ? FoundTombstone : ThisBucket;
  36. return false;
  37. }
  38. // If this is a tombstone, remember it. If Val ends up not in the map, we
  39. // prefer to return it than something that would require more probing.
  40. // Ditto for zero values.
  41. //哈希冲突,再次哈希
  42. if (KeyInfoT::isEqual(ThisBucket->getFirst(), TombstoneKey) &&
  43. !FoundTombstone)
  44. FoundTombstone = ThisBucket; // Remember the first tombstone found.
  45. if (ValueInfoT::isPurgeable(ThisBucket->getSecond()) && !FoundTombstone)
  46. FoundTombstone = ThisBucket;
  47. // Otherwise, it's a hash collision or a tombstone, continue quadratic
  48. // probing.
  49. if (ProbeAmt > NumBuckets) {
  50. FatalCorruptHashTables(BucketsPtr, NumBuckets);
  51. }
  52. BucketNo += ProbeAmt++;
  53. BucketNo &= (NumBuckets-1);
  54. }
  55. }
  • 哈希函数,计算下标
  • 内存平移,获取Bucket
  • 找到Bucket,直接返回
  • Bucket为空,返回默认值
  • 哈希冲突,再次哈希

LookupBucketFor函数二,Bucket未使用const修饰

  1. template <typename LookupKeyT>
  2. bool LookupBucketFor(const LookupKeyT &Val, BucketT *&FoundBucket) {
  3. //创建空Bucket
  4. const BucketT *ConstFoundBucket;
  5. //调用LookupBucketFor函数一
  6. bool Result = const_cast<const DenseMapBase *>(this)
  7. ->LookupBucketFor(Val, ConstFoundBucket);
  8. //将ConstFoundBucket赋值FoundBucket
  9. FoundBucket = const_cast<BucketT *>(ConstFoundBucket);
  10. //返回结果,是否查找到Bucket
  11. return Result;
  12. }
  • 创建空Bucket
  • 调用LookupBucketFor函数一
  • ConstFoundBucket赋值FoundBucket
  • 返回结果,是否查找到Bucket

3.2.6 InsertIntoBucket函数

  1. template <typename KeyArg, typename... ValueArgs>
  2. BucketT *InsertIntoBucket(BucketT *TheBucket, KeyArg &&Key,
  3. ValueArgs &&... Values) {
  4. TheBucket = InsertIntoBucketImpl(Key, Key, TheBucket);
  5. TheBucket->getFirst() = std::forward<KeyArg>(Key);
  6. ::new (&TheBucket->getSecond()) ValueT(std::forward<ValueArgs>(Values)...);
  7. return TheBucket;
  8. }
  • 调用InsertIntoBucketImpl函数

InsertIntoBucketImpl函数

  1. template <typename LookupKeyT>
  2. BucketT *InsertIntoBucketImpl(const KeyT &Key, const LookupKeyT &Lookup,
  3. BucketT *TheBucket) {
  4. // If the load of the hash table is more than 3/4, or if fewer than 1/8 of
  5. // the buckets are empty (meaning that many are filled with tombstones),
  6. // grow the table.
  7. //
  8. // The later case is tricky. For example, if we had one empty bucket with
  9. // tons of tombstones, failing lookups (e.g. for insertion) would have to
  10. // probe almost the entire table until it found the empty bucket. If the
  11. // table completely filled with tombstones, no lookup would ever succeed,
  12. // causing infinite loops in lookup.
  13. unsigned NewNumEntries = getNumEntries() + 1;
  14. unsigned NumBuckets = getNumBuckets();
  15. if (LLVM_UNLIKELY(NewNumEntries * 4 >= NumBuckets * 3)) {
  16. this->grow(NumBuckets * 2);
  17. LookupBucketFor(Lookup, TheBucket);
  18. NumBuckets = getNumBuckets();
  19. } else if (LLVM_UNLIKELY(NumBuckets-(NewNumEntries+getNumTombstones()) <=
  20. NumBuckets/8)) {
  21. this->grow(NumBuckets);
  22. LookupBucketFor(Lookup, TheBucket);
  23. }
  24. ASSERT(TheBucket);
  25. // Only update the state after we've grown our bucket space appropriately
  26. // so that when growing buckets we have self-consistent entry count.
  27. // If we are writing over a tombstone or zero value, remember this.
  28. if (KeyInfoT::isEqual(TheBucket->getFirst(), getEmptyKey())) {
  29. // Replacing an empty bucket.
  30. incrementNumEntries();
  31. } else if (KeyInfoT::isEqual(TheBucket->getFirst(), getTombstoneKey())) {
  32. // Replacing a tombstone.
  33. incrementNumEntries();
  34. decrementNumTombstones();
  35. } else {
  36. // we should be purging a zero. No accounting changes.
  37. ASSERT(ValueInfoT::isPurgeable(TheBucket->getSecond()));
  38. TheBucket->getSecond().~ValueT();
  39. }
  40. return TheBucket;
  41. }
  • 如果哈希表的负载大于3/4,两倍扩容

3.3 _object_get_associative_reference

  1. id
  2. _object_get_associative_reference(id object, const void *key)
  3. {
  4. ObjcAssociation association{};
  5. {
  6. //调用构造函数,加锁。函数作用域结束AssociationsManager释放,调用析构函数,解锁
  7. AssociationsManager manager;
  8. //获取唯⼀的全局静态哈希Map
  9. AssociationsHashMap &associations(manager.get());
  10. //通过关联对象寻址Iterator
  11. AssociationsHashMap::iterator i = associations.find((objc_object *)object);
  12. if (i != associations.end()) {
  13. //获取ObjectAssociationMap
  14. ObjectAssociationMap &refs = i->second;
  15. //通过key寻址Iterator
  16. ObjectAssociationMap::iterator j = refs.find(key);
  17. if (j != refs.end()) {
  18. //获取ObjcAssociation
  19. association = j->second;
  20. //保留返回值
  21. association.retainReturnedValue();
  22. }
  23. }
  24. }
  25. //生成自动返回值
  26. return association.autoreleaseReturnedValue();
  27. }

【第一步】

  • 调用构造函数,加锁。函数作用域结束AssociationsManager释放,调用析构函数,解锁

  • AssociationsHashMap为单例,在内存中只有一份

【第二步】

  • 通过关联对象寻址Iterator

  • 获取ObjectAssociationMap

【第三步】

  • 通过key寻址Iterator

  • 获取ObjcAssociation

  • 保留返回值

【第四步】

  • 生成自动返回值

3.4 关联对象的销毁

关联对象的销毁,依赖于被关联对象的生命周期。当被关联对象被销毁时,它的关联对象会自动销毁

对象销毁流程:

  • C++函数释放 :objc_cxxDestruct
  • 关联对象销毁:_object_remove_assocations
  • 弱引用销毁:weak_clear_no_lock
  • 清空引用计数:table.refcnts.erase
  • 销毁对象:free

3.4.1 dealloc

  1. - (void)dealloc {
  2. _objc_rootDealloc(self);
  3. }

3.4.2 _objc_rootDealloc

  1. void
  2. _objc_rootDealloc(id obj)
  3. {
  4. ASSERT(obj);
  5. obj->rootDealloc();
  6. }

3.4.3 rootDealloc

  1. inline void
  2. objc_object::rootDealloc()
  3. {
  4. if (isTaggedPointer()) return; // fixme necessary?
  5. if (fastpath(isa.nonpointer &&
  6. !isa.weakly_referenced &&
  7. !isa.has_assoc &&
  8. #if ISA_HAS_CXX_DTOR_BIT
  9. !isa.has_cxx_dtor &&
  10. #else
  11. !isa.getClass(false)->hasCxxDtor() &&
  12. #endif
  13. !isa.has_sidetable_rc))
  14. {
  15. assert(!sidetable_present());
  16. free(this);
  17. }
  18. else {
  19. object_dispose((id)this);
  20. }
  21. }
  • 判断C++析构函数的调用、关联对象的销毁、弱引用的销毁、引用计数是否清空,全部符合条件,调用free函数销毁对象
  • 否则,调用object_dispose函数,继续销毁流程

3.4.4 object_dispose

  1. id
  2. object_dispose(id obj)
  3. {
  4. if (!obj) return nil;
  5. objc_destructInstance(obj);
  6. free(obj);
  7. return nil;
  8. }

3.4.5 objc_destructInstance

  1. void *objc_destructInstance(id obj)
  2. {
  3. if (obj) {
  4. // Read all of the flags at once for performance.
  5. bool cxx = obj->hasCxxDtor();
  6. bool assoc = obj->hasAssociatedObjects();
  7. // This order is important.
  8. if (cxx) object_cxxDestruct(obj);
  9. if (assoc) _object_remove_assocations(obj, /*deallocating*/true);
  10. obj->clearDeallocating();
  11. }
  12. return obj;
  13. }
  • object_cxxDestruct:调用C++的析构函数
  • _object_remove_assocations:关联对象销毁
  • clearDeallocating,弱引用销毁、清空引用计数

3.4.6 _object_remove_assocations

  1. void
  2. _object_remove_assocations(id object, bool deallocating)
  3. {
  4. ObjectAssociationMap refs{};
  5. {
  6. //调用构造函数,加锁。函数作用域结束AssociationsManager释放,调用析构函数,解锁
  7. AssociationsManager manager;
  8. //获取唯⼀的全局静态哈希Map
  9. AssociationsHashMap &associations(manager.get());
  10. //通过关联对象寻址Iterator
  11. AssociationsHashMap::iterator i = associations.find((objc_object *)object);
  12. if (i != associations.end()) {
  13. //替换
  14. refs.swap(i->second);
  15. // If we are not deallocating, then SYSTEM_OBJECT associations are preserved.
  16. bool didReInsert = false;
  17. if (!deallocating) {
  18. for (auto &ref: refs) {
  19. if (ref.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
  20. i->second.insert(ref);
  21. didReInsert = true;
  22. }
  23. }
  24. }
  25. //移除Iterator
  26. if (!didReInsert)
  27. associations.erase(i);
  28. }
  29. }
  30. // Associations to be released after the normal ones.
  31. SmallVector<ObjcAssociation *, 4> laterRefs;
  32. // release everything (outside of the lock).
  33. for (auto &i: refs) {
  34. if (i.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
  35. // If we are not deallocating, then RELEASE_LATER associations don't get released.
  36. if (deallocating)
  37. laterRefs.append(&i.second);
  38. } else {
  39. i.second.releaseHeldValue();
  40. }
  41. }
  42. //循环销毁
  43. for (auto *later: laterRefs) {
  44. later->releaseHeldValue();
  45. }
  46. }

关联对象销毁流程:dealloc_objc_rootDeallocrootDeallocobject_disposeobjc_destructInstance_object_remove_assocations

3.4.7 clearDeallocating

  1. inline void
  2. objc_object::clearDeallocating()
  3. {
  4. if (slowpath(!isa.nonpointer)) {
  5. // Slow path for raw pointer isa.
  6. sidetable_clearDeallocating();
  7. }
  8. else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
  9. // Slow path for non-pointer isa with weak refs and/or side table data.
  10. clearDeallocating_slow();
  11. }
  12. assert(!sidetable_present());
  13. }

3.4.8 clearDeallocating_slow

  1. NEVER_INLINE void
  2. objc_object::clearDeallocating_slow()
  3. {
  4. ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
  5. SideTable& table = SideTables()[this];
  6. table.lock();
  7. if (isa.weakly_referenced) {
  8. weak_clear_no_lock(&table.weak_table, (id)this);
  9. }
  10. if (isa.has_sidetable_rc) {
  11. table.refcnts.erase(this);
  12. }
  13. table.unlock();
  14. }
  • weak_clear_no_lock:弱引用销毁
  • table.refcnts.erase:清空引用计数

总结

类扩展 VS 分类:

  • 类扩展(Extension
    • 特殊的分类,也称作匿名分类
    • 可以给类添加成员属性,但是为私有变量
    • 可以给类添加方法,也是私有方法
  • 分类(Category
    • 专门用来给类添加新的方法
    • 不能给类添加成员属性,添加后无法读取(可通过Runtime给分类添加属性)
    • 分类中使用@property定义变量,只会生成变量的getter/setter方法的声明,不能生成方法的实现和下划线开头的成员变量

类扩展的分析:

  • 所以类扩展并不会影响主类的加载流程,在编译时,类扩展中的属性和方法和主类融为一体
  • 创建的类扩展只有·h文件,所以只做声明,实现需要依赖于当前的主类

关联对象:

  • 分类中,不能直接使用属性的getter/setter方法,只能使用关联对象的方式实现成员变量的效果
  • objc_setAssociatedObject
    • object:被关联者
    • key:标识符
    • value:值
    • policy:关联策略
  • objc_removeAssociatedObjects
    • object:被关联者
    • key:标识符
  • 关联策略:nonatomicatomicassignretaincopy等修饰

objc_setAssociatedObject & objc_getAssociatedObject

  • 函数为中间层层代码,目的是对业务层进行隔离。因为底层API根据不同版本会发生变化,但中间层代码是保持不变的,所以开发者不用担心因底层API变化导致程序的修改

_object_set_associative_reference

【步骤一】

  • 将不同类型的object,保存成统一的DisguisedPtr<objc_object>结构
  • 将关联策略和值,以面向对象的方式进行存储ObjcAssociation类中
  • 根据不同的关联策略进行处理

【步骤二】

  • 调用构造函数,加锁。函数作用域结束AssociationsManager释放,调用析构函数,解锁
  • AssociationsHashMap为单例,在内存中只有一份

【步骤三】

  • 如果value存在,进行关联
    • 调用AssociationsHashMaptry_emplace函数,将objectObjectAssociationMap关联
    • 返回结果refs_result,如果是首次关联,打标记
    • refs_result中找到object关联的Bucket
    • 调用Buckettry_emplace函数,将key关联ObjcAssociation
    • 如果存在旧值,替换为新值
  • 如果传入的value不存在,移除关联
    • 通过关联对象寻址Iterator
    • 获取ObjectAssociationMap
    • 替换ObjcAssociation,新值替换旧值
    • 移除旧值
    • 如果关联对象关联的Iterator为空,直接移除

【步骤四】

  • nonpointerIsa中标记,当对象销毁时,关联对象也要跟随销毁
  • 释放旧值

_object_get_associative_reference

【第一步】

  • 调用构造函数,加锁。函数作用域结束AssociationsManager释放,调用析构函数,解锁
  • AssociationsHashMap为单例,在内存中只有一份

【第二步】

  • 通过关联对象寻址Iterator
  • 获取ObjectAssociationMap

【第三步】

  • 通过key寻址Iterator
  • 获取ObjcAssociation
  • 保留返回值

【第四步】

  • 生成自动返回值

关联对象的销毁:

  • 关联对象的销毁,依赖于被关联对象的生命周期。当被关联对象被销毁时,它的关联对象会自动销毁
  • 对象销毁流程:
    • C++函数释放 :objc_cxxDestruct
    • 关联对象销毁:_object_remove_assocations
    • 弱引用销毁:weak_clear_no_lock
    • 清空引用计数:table.refcnts.erase
    • 销毁对象:free
  • 销毁流程:dealloc_objc_rootDeallocrootDeallocobject_disposeobjc_destructInstance_object_remove_assocations

AssociationsHashMap

  • 单例,使用static修饰,全局静态对象
  • 双层HashMap结构

AssociationsManager

  • 可创建多份,并为唯一
  • AssociationsManager():构造函数,加锁
  • ~AssociationsManager():析构函数,解锁
  • 加锁、解锁操作,保证对象的安全性,防止冲突

AssociationsManager的初始化:

  • 初始化流程:map_imagesmap_images_nolockarr_init_objc_associations_init
  • map_images时,系统创建AssociationsHashMap关联对象表。和它一起被创建的全局系统表,还有AutoreleasePoolPage自动释放池,以及SideTablesMap散列表
  • SideTablesMap散列表中包含两张子表,分别是RefcountMap引用计数表,还有weak_table_t弱引用表