IMG_5671.JPG

类扩展VS分类

1:category:类别,分类

  • 专门用来给类添加新的方法
  • 不能给类添加成员属性,添加了成员变量,也无法取到
  • 注意:其实可以通过runtime给分类添加属性
  • 分类中用@property定义变量,只会生成变量的getter,setter方法的声明,不能生成方法实现和带下划线的成员变量

2:extension: 类扩展

  • 可以说成是特殊的分类,也称作匿名分类,没有名字的分类,如@interface,一定是写在声明之后,实现之前
  • 可以给类添加成员属性,但是是私有的变量
  • 可以给类添加方法,也是私有方法

    分析

  • 类扩展作为类的部分内容加载进来

  • 建立分类,断点lldb调试查看ro的方法列表,发现ext类方法已经加入到方法列表中

    1. (lldb) p ro.baseMethods()
    2. (method_list_t *) $0 = 0x00000001000041f0
    3. Fix-it applied, fixed expression was:
    4. ro->baseMethods()
    5. (lldb) p *$0
    6. (method_list_t) $1 = {
    7. entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 14)
    8. }
    9. (lldb) p $1.get(0).big()
    10. (method_t::big) $2 = {
    11. name = "saySomething"
    12. types = 0x0000000100003d8b "v16@0:8"
    13. imp = 0x0000000100003820 (KCObjcBuild`-[LGPerson(LGA) saySomething])
    14. }
    15. (lldb) p $1.get(1).big()
    16. (method_t::big) $3 = {
    17. name = "cateA_instanceMethod1"
    18. types = 0x0000000100003d8b "v16@0:8"
    19. imp = 0x0000000100003850 (KCObjcBuild`-[LGPerson(LGA) cateA_instanceMethod1])
    20. }
    21. (lldb) p $1.get(2).big()
    22. (method_t::big) $4 = {
    23. name = "cateA_instanceMethod2"
    24. types = 0x0000000100003d8b "v16@0:8"
    25. imp = 0x0000000100003880 (KCObjcBuild`-[LGPerson(LGA) cateA_instanceMethod2])
    26. }
    27. (lldb) p $1.get(3).big()
    28. (method_t::big) $5 = {
    29. name = "saySomething"
    30. types = 0x0000000100003d8b "v16@0:8"
    31. imp = 0x0000000100003950 (KCObjcBuild`-[LGPerson saySomething])
    32. }
    33. (lldb) p $1.get(4).big()
    34. (method_t::big) $6 = {
    35. name = "sayHello1"
    36. types = 0x0000000100003d8b "v16@0:8"
    37. imp = 0x0000000100003980 (KCObjcBuild`-[LGPerson sayHello1])
    38. }
    39. (lldb) p $1.get(5).big()
    40. (method_t::big) $7 = {
    41. name = "ext_instanceMethod"
    42. types = 0x0000000100003d8b "v16@0:8"
    43. imp = 0x00000001000039b0 (KCObjcBuild`-[LGPerson ext_instanceMethod])
    44. }
    45. (lldb) p $1.get(6).big()
    46. (method_t::big) $8 = {
    47. name = "name"
    48. types = 0x0000000100003d9f "@16@0:8"
    49. imp = 0x00000001000039e0 (KCObjcBuild`-[LGPerson name])
    50. }
    51. (lldb)

    小结

  • 类的扩展 在编译期 会作为类的一部分,和类一起编译进来

  • 类的扩展只是 声明 ,依赖于 本类 的实现。

    关联对象

    结构图

  • 双层hashMap结构

    • 第一层,key是对象地址,value是ObjectAssociationMap
    • 第二次,ObjectAssociationMap存了很多个bucket,每个bucket的key是name,value是objectAssocation

未命名文件 (2).jpg

说明

  • 动态添加的成员变量,没有get、set方法,存储object - name -> value
  • policy缓存策略
  • _object_set_associative_reference底层实现,objc_setAssociatedObject隔绝业务层,底层变化不影响业务层

    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. }
  • DisguisedPtr disguised{(objc_object *)object} 对objcect进行包装,包装成统一数据类型,方便使用

  • ObjcAssociation association{policy, value},对policy和value进行包装,也包装成统一的数据类型Association

    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. DisguisedPtr<objc_object> disguised{(objc_object *)object};
    11. ObjcAssociation association{policy, value};
    12. // retain the new value (if any) outside the lock.
    13. association.acquireValue();
    14. bool isFirstAssociation = false;
    15. {
    16. AssociationsManager manager;
    17. AssociationsHashMap &associations(manager.get());
    18. if (value) {
    19. auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
    20. if (refs_result.second) {
    21. /* it's the first association we make */
    22. isFirstAssociation = true;
    23. }
    24. /* establish or replace the association */
    25. auto &refs = refs_result.first->second;
    26. auto result = refs.try_emplace(key, std::move(association));
    27. if (!result.second) {
    28. association.swap(result.first->second);
    29. }
    30. } else {
    31. auto refs_it = associations.find(disguised);
    32. if (refs_it != associations.end()) {
    33. auto &refs = refs_it->second;
    34. auto it = refs.find(key);
    35. if (it != refs.end()) {
    36. association.swap(it->second);
    37. refs.erase(it);
    38. if (refs.size() == 0) {
    39. associations.erase(refs_it);
    40. }
    41. }
    42. }
    43. }
    44. }
    45. // Call setHasAssociatedObjects outside the lock, since this
    46. // will call the object's _noteAssociatedObjects method if it
    47. // has one, and this may trigger +initialize which might do
    48. // arbitrary stuff, including setting more associated objects.
    49. if (isFirstAssociation)
    50. object->setHasAssociatedObjects();
    51. // release the old value (outside of the lock).
    52. association.releaseHeldValue();
    53. }

    实例

  • 建立person类,定义person分类,在分类中定义cate_name属性,重写set、get方法,通过objc_setAssociatedObject和objc_getAssociatedObject存储 ```cpp

  • (void)setCate_name:(NSString )cate_name{ /** 1: 对象 2: 标识符 3: value 4: 策略 / objc_setAssociatedObject(self, “cate_name”, cate_name, OBJC_ASSOCIATION_COPY_NONATOMIC); }

  • (NSString *)cate_name{ return objc_getAssociatedObject(self, “cate_name”); } ```

  • lldb调试,查看disguised和Association

    1. (lldb) p disguised
    2. (DisguisedPtr<objc_object>) $0 = (value = 18446744069265650576)
    3. (lldb) p association
    4. (objc::ObjcAssociation) $1 = {
    5. _policy = 3
    6. _value = 0x0000000100004080 "KC"
    7. }
  • AssociationsManager

    • 作用域->所属{}内
    • 构造函数-> AssociationsManager() { AssociationsManagerLock.lock(); }
    • 析构函数-> ~AssociationsManager() { AssociationsManagerLock.unlock(); }
  • AssociationsHashMap
    • 单例,_mapStorage全局静态变量,AssociationsHashMap唯一,多个manager调用时都是调用唯一的AssociationsHashMap表 ```cpp class AssociationsManager { using Storage = ExplicitInitDenseMap, ObjectAssociationMap>; static Storage _mapStorage;

public: AssociationsManager() { AssociationsManagerLock.lock(); } ~AssociationsManager() { AssociationsManagerLock.unlock(); }

  1. AssociationsHashMap &get() {
  2. return _mapStorage.get();
  3. }
  4. static void init() {
  5. _mapStorage.init();
  6. }

};

  1. - LLDB调试associationsrefs_result
  2. - refs_resultstd代表类型,hashMap底层的存储结构
  3. ```cpp
  4. (lldb) p associations
  5. (objc::AssociationsHashMap) $0 = {
  6. Buckets = nil
  7. NumEntries = 0
  8. NumTombstones = 0
  9. NumBuckets = 0
  10. }
  11. (lldb) p refs_result
  12. (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>) $1 = {
  13. first = {
  14. Ptr = 0x0000000108d38f60
  15. End = 0x0000000108d38fe0
  16. }
  17. second = true
  18. }
  • try_emplace底层结构

    • 查询是否存在bucket,没有就根据key(上述包装的disguised结构地址)创建

      1. template <typename... Ts>
      2. std::pair<iterator, bool> try_emplace(const KeyT &Key, Ts &&... Args) {
      3. BucketT *TheBucket;
      4. if (LookupBucketFor(Key, TheBucket))
      5. return std::make_pair(
      6. makeIterator(TheBucket, getBucketsEnd(), true),
      7. false); // Already in map.
      8. // Otherwise, insert the new element.
      9. TheBucket = InsertIntoBucket(TheBucket, Key, std::forward<Ts>(Args)...);
      10. return std::make_pair(
      11. makeIterator(TheBucket, getBucketsEnd(), true),
      12. true);
      13. }

      关联对象设置流程

  1. 将object包装成disguised对象,然后对policy和value做处理,生成Association
  2. 查找到全局静态HashMap表
  3. 判断是否为第一次进来
    1. 如果是第一次进来,则创建bucket空桶
    2. 如果不是第一次,则将Association插入到bucket(ObjcetAssociationMap)中去
      1. key对应const void *,Value对应ObjcetAssociation
      2. TheBucket->getFirst()对应为key值
      3. TheBucket->getSecond()对应为Value值
  4. 创建一个空的ObjectAssociationMap去取查询的键值对
  5. 如果发现没有这个key就插入一个空的BucketT进去 返回
  6. 标记对象存在关联对象
  7. 用当前修饰策略和值组成一个ObjcAssociation替换原来BucketT中的值
  8. 标记一个ObjectAssociationMap的第一次为false

    关联对象插入空流程

  • 根据DisguisePtr找到AssociationHashMap中的iterator迭代查询器
  • 清理迭代器
  • 如果插入空值,相当于清空

    关联对象取值流程

  • 创建一个AssociationsManager管理类

  • 获取唯一的全局静态HashMap
  • 根据DisguisedPtr找到AssociationsHashMap中的iterator迭代查询器
  • 如果这个迭代查询器不是最后一个,获取ObjectAssociationMap
  • 找到ObjectAssociationMap的迭代查询器获取一个经过属性修饰符修饰的value
  • 返回value

    补充

  • static void init() ,方法前加static代表类方法 ```cpp class AssociationsManager { using Storage = ExplicitInitDenseMap, ObjectAssociationMap>; static Storage _mapStorage;

public: AssociationsManager() { AssociationsManagerLock.lock(); } ~AssociationsManager() { AssociationsManagerLock.unlock(); }

  1. AssociationsHashMap &get() {
  2. return _mapStorage.get();
  3. }
  4. static void init() {
  5. _mapStorage.init();
  6. }

};

  1. - **系统全局的表**
  2. - **散列表SideTablesMap (引用计数表、弱引用表)**
  3. - **自动释放池 AutoreleasePoolPage**
  4. - **关联对象表**
  5. - 类表,收集全局的类
  6. - AutoreleasePoolPage内存页的初始化
  7. - SideTablesMap散列表初始化(包含了引用计数表和弱引用表)
  8. - _objc_associations_init关联对象表初始化
  9. ```cpp
  10. void arr_init(void)
  11. {
  12. AutoreleasePoolPage::init();
  13. SideTablesMap.init();
  14. _objc_associations_init();
  15. }
  • 生命周期与object有关,在object调用dealloc时,调用_object_remove_assocations移除关联对象 ```cpp void _object_remove_assocations(id object, bool deallocating) { ObjectAssociationMap refs{};

    {

    1. AssociationsManager manager;
    2. AssociationsHashMap &associations(manager.get());
    3. AssociationsHashMap::iterator i = associations.find((objc_object *)object);
    4. if (i != associations.end()) {
    5. refs.swap(i->second);
    6. // If we are not deallocating, then SYSTEM_OBJECT associations are preserved.
    7. bool didReInsert = false;
    8. if (!deallocating) {
    9. for (auto &ref: refs) {
    10. if (ref.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
    11. i->second.insert(ref);
    12. didReInsert = true;
    13. }
    14. }
    15. }
    16. if (!didReInsert)
    17. associations.erase(i);
    18. }

    }

    // Associations to be released after the normal ones. SmallVector laterRefs;

    // release everything (outside of the lock). for (auto &i: refs) {

    1. if (i.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
    2. // If we are not deallocating, then RELEASE_LATER associations don't get released.
    3. if (deallocating)
    4. laterRefs.append(&i.second);
    5. } else {
    6. i.second.releaseHeldValue();
    7. }

    } for (auto *later: laterRefs) {

    1. later->releaseHeldValue();

    } }

```