类扩展VS分类
1:category:类别,分类
- 专门用来给类添加新的方法
- 不能给类添加成员属性,添加了成员变量,也无法取到
- 注意:其实可以通过runtime给分类添加属性
- 分类中用@property定义变量,只会生成变量的getter,setter方法的声明,不能生成方法实现和带下划线的成员变量
2:extension: 类扩展
- 可以说成是特殊的分类,也称作匿名分类,没有名字的分类,如@interface,一定是写在声明之后,实现之前
- 可以给类添加成员属性,但是是私有的变量
-
分析
类扩展作为类的部分内容加载进来
建立分类,断点lldb调试查看ro的方法列表,发现ext类方法已经加入到方法列表中
(lldb) p ro.baseMethods()
(method_list_t *) $0 = 0x00000001000041f0
Fix-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隔绝业务层,底层变化不影响业务层
void
objc_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/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));
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 = nil
NumEntries = 0
NumTombstones = 0
NumBuckets = 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 = 0x0000000108d38f60
End = 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关联对象表初始化
```cpp
void 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();
} }
```