类扩展VS分类
1:category:类别,分类
- 专门用来给类添加新的方法
- 不能给类添加成员属性,添加了成员变量,也无法取到
- 注意:其实可以通过runtime给分类添加属性
- 分类中用@property定义变量,只会生成变量的getter,setter方法的声明,不能生成方法实现和带下划线的成员变量
2:extension: 类扩展
- 可以说成是特殊的分类,也称作匿名分类,没有名字的分类,如@interface,一定是写在声明之后,实现之前
- 可以给类添加成员属性,但是是私有的变量
-
分析
类扩展作为类的部分内容加载进来
建立分类,断点lldb调试查看ro的方法列表,发现ext类方法已经加入到方法列表中
(lldb) p ro.baseMethods()(method_list_t *) $0 = 0x00000001000041f0Fix-it applied, fixed expression was:ro->baseMethods()(lldb) p *$0(method_list_t) $1 = {entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 14)}(lldb) p $1.get(0).big()(method_t::big) $2 = {name = "saySomething"types = 0x0000000100003d8b "v16@0:8"imp = 0x0000000100003820 (KCObjcBuild`-[LGPerson(LGA) saySomething])}(lldb) p $1.get(1).big()(method_t::big) $3 = {name = "cateA_instanceMethod1"types = 0x0000000100003d8b "v16@0:8"imp = 0x0000000100003850 (KCObjcBuild`-[LGPerson(LGA) cateA_instanceMethod1])}(lldb) p $1.get(2).big()(method_t::big) $4 = {name = "cateA_instanceMethod2"types = 0x0000000100003d8b "v16@0:8"imp = 0x0000000100003880 (KCObjcBuild`-[LGPerson(LGA) cateA_instanceMethod2])}(lldb) p $1.get(3).big()(method_t::big) $5 = {name = "saySomething"types = 0x0000000100003d8b "v16@0:8"imp = 0x0000000100003950 (KCObjcBuild`-[LGPerson saySomething])}(lldb) p $1.get(4).big()(method_t::big) $6 = {name = "sayHello1"types = 0x0000000100003d8b "v16@0:8"imp = 0x0000000100003980 (KCObjcBuild`-[LGPerson sayHello1])}(lldb) p $1.get(5).big()(method_t::big) $7 = {name = "ext_instanceMethod"types = 0x0000000100003d8b "v16@0:8"imp = 0x00000001000039b0 (KCObjcBuild`-[LGPerson ext_instanceMethod])}(lldb) p $1.get(6).big()(method_t::big) $8 = {name = "name"types = 0x0000000100003d9f "@16@0:8"imp = 0x00000001000039e0 (KCObjcBuild`-[LGPerson name])}(lldb)
小结
类的扩展 在编译期 会作为类的一部分,和类一起编译进来
-
关联对象
结构图
双层hashMap结构
- 第一层,key是对象地址,value是ObjectAssociationMap
- 第二次,ObjectAssociationMap存了很多个bucket,每个bucket的key是name,value是objectAssocation
说明
- 动态添加的成员变量,没有get、set方法,存储object - name -> value
- policy缓存策略
_object_set_associative_reference底层实现,objc_setAssociatedObject隔绝业务层,底层变化不影响业务层
voidobjc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy){_object_set_associative_reference(object, key, value, policy);}
DisguisedPtr
disguised{(objc_object *)object} 对objcect进行包装,包装成统一数据类型,方便使用 ObjcAssociation association{policy, value},对policy和value进行包装,也包装成统一的数据类型Association
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/44094390if (!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();bool isFirstAssociation = false;{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 */isFirstAssociation = true;}/* 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);}}}}}// 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();}
实例
建立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
(lldb) p disguised(DisguisedPtr<objc_object>) $0 = (value = 18446744069265650576)(lldb) p association(objc::ObjcAssociation) $1 = {_policy = 3_value = 0x0000000100004080 "KC"}
AssociationsManager
- 作用域->所属{}内
- 构造函数-> AssociationsManager() { AssociationsManagerLock.lock(); }
- 析构函数-> ~AssociationsManager() { AssociationsManagerLock.unlock(); }
- AssociationsHashMap
- 单例,_mapStorage全局静态变量,AssociationsHashMap唯一,多个manager调用时都是调用唯一的AssociationsHashMap表
```cpp
class AssociationsManager {
using Storage = ExplicitInitDenseMap
, ObjectAssociationMap>; static Storage _mapStorage;
- 单例,_mapStorage全局静态变量,AssociationsHashMap唯一,多个manager调用时都是调用唯一的AssociationsHashMap表
```cpp
class AssociationsManager {
using Storage = ExplicitInitDenseMap
public: AssociationsManager() { AssociationsManagerLock.lock(); } ~AssociationsManager() { AssociationsManagerLock.unlock(); }
AssociationsHashMap &get() {return _mapStorage.get();}static void init() {_mapStorage.init();}
};
- LLDB调试associations、refs_result- refs_result,std代表类型,hashMap底层的存储结构```cpp(lldb) p associations(objc::AssociationsHashMap) $0 = {Buckets = nilNumEntries = 0NumTombstones = 0NumBuckets = 0}(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>) $1 = {first = {Ptr = 0x0000000108d38f60End = 0x0000000108d38fe0}second = true}
try_emplace底层结构
查询是否存在bucket,没有就根据key(上述包装的disguised结构地址)创建
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); // 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);}
关联对象设置流程
- 将object包装成disguised对象,然后对policy和value做处理,生成Association
- 查找到全局静态HashMap表
- 判断是否为第一次进来
- 如果是第一次进来,则创建bucket空桶
- 如果不是第一次,则将Association插入到bucket(ObjcetAssociationMap)中去
- key对应const void *,Value对应ObjcetAssociation
- TheBucket->getFirst()对应为key值
- TheBucket->getSecond()对应为Value值
- 创建一个空的ObjectAssociationMap去取查询的键值对
- 如果发现没有这个key就插入一个空的BucketT进去 返回
- 标记对象存在关联对象
- 用当前修饰策略和值组成一个ObjcAssociation替换原来BucketT中的值
- 标记一个ObjectAssociationMap的第一次为false
关联对象插入空流程
- 根据DisguisePtr找到AssociationHashMap中的iterator迭代查询器
- 清理迭代器
-
关联对象取值流程
创建一个AssociationsManager管理类
- 获取唯一的全局静态HashMap
- 根据DisguisedPtr找到AssociationsHashMap中的iterator迭代查询器
- 如果这个迭代查询器不是最后一个,获取ObjectAssociationMap
- 找到ObjectAssociationMap的迭代查询器获取一个经过属性修饰符修饰的value
-
补充
static void init() ,方法前加static代表类方法 ```cpp class AssociationsManager { using Storage = ExplicitInitDenseMap
, ObjectAssociationMap>; static Storage _mapStorage;
public: AssociationsManager() { AssociationsManagerLock.lock(); } ~AssociationsManager() { AssociationsManagerLock.unlock(); }
AssociationsHashMap &get() {return _mapStorage.get();}static void init() {_mapStorage.init();}
};
- **系统全局的表**- **散列表SideTablesMap (引用计数表、弱引用表)**- **自动释放池 AutoreleasePoolPage**- **关联对象表**- 类表,收集全局的类- AutoreleasePoolPage内存页的初始化- SideTablesMap散列表初始化(包含了引用计数表和弱引用表)- _objc_associations_init关联对象表初始化```cppvoid arr_init(void){AutoreleasePoolPage::init();SideTablesMap.init();_objc_associations_init();}
生命周期与object有关,在object调用dealloc时,调用_object_remove_assocations移除关联对象 ```cpp void _object_remove_assocations(id object, bool deallocating) { ObjectAssociationMap refs{};
{
AssociationsManager manager;AssociationsHashMap &associations(manager.get());AssociationsHashMap::iterator i = associations.find((objc_object *)object);if (i != associations.end()) {refs.swap(i->second);// If we are not deallocating, then SYSTEM_OBJECT associations are preserved.bool didReInsert = false;if (!deallocating) {for (auto &ref: refs) {if (ref.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {i->second.insert(ref);didReInsert = true;}}}if (!didReInsert)associations.erase(i);}
}
// Associations to be released after the normal ones. SmallVector
laterRefs; // release everything (outside of the lock). for (auto &i: refs) {
if (i.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {// If we are not deallocating, then RELEASE_LATER associations don't get released.if (deallocating)laterRefs.append(&i.second);} else {i.second.releaseHeldValue();}
} for (auto *later: laterRefs) {
later->releaseHeldValue();
} }
```
