类的扩展和分类
-
category
:分类、类别
- 给类增加方法
- 不能添加成员变量
- 可以使用
runtime
给分类添加属性 - 分类中添加的属性指挥生成对应的
setter
、getter
方法的声明,不能生成方法实现和带下划线的成员变量。
-
extension
:类扩展
- 可以添加成员属性,属于私有。
- 可以添加方法,属于私有。
关于类的扩展,是放在类的声明之后类的实现之前。类的扩展里面放的多是私有方法和成员变量。类的扩展会在编译期就作为类的一部分写入到类信息中。另外,类的扩展只是一个声明,它依赖主类需要在主类内部完成实现。
关联对象
分类不能添加属性,它只是声明了setter
、getter
方法,没有实现,那么就需要借助关联对象来实现这个功能。
分类.h中
@property (nonatomic, copy) NSString *name;
分类.m中
- (void)setCate_name:(NSString *)name{
/**
(对象,标识符,value, 策略)
*/
objc_setAssociatedObject(self, "name", cate_name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name{
return objc_getAssociatedObject(self, "name");
}
关联对象源码探索
关联对象如何运作的
static void
_base_objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
_object_set_associative_reference(object, key, value, policy);
}
static ChainedHookFunction<objc_hook_setAssociatedObject> SetAssocHook{_base_objc_setAssociatedObject};
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
SetAssocHook.get()(object, key, value, policy);
}
关联对象设值底层调用的方法就是_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
...
DisguisedPtr<objc_object> disguised{(objc_object *)object};
ObjcAssociation association{policy, value};
association.acquireValue();
//工作代码块
{代码块}
association.releaseHeldValue();
}
传递进来的object
被包装成了DisguisedPtr
,对应的value
和policy
打包成了ObjcAssociation
。
然后具体的操作放在了代码块中
association.acquireValue();
{
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 */
object->setHasAssociatedObjects();
}
/* 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);
}
}
}
}
}
-
acquireValue()
更具传递进来的polic
对值进行处理
inline void acquireValue() {
if (_value) {
switch (_policy & 0xFF) {
case OBJC_ASSOCIATION_SETTER_RETAIN:
_value = objc_retain(_value);
break;
case OBJC_ASSOCIATION_SETTER_COPY:
_value = ((id(*)(id, SEL))objc_msgSend)(_value, @selector(copy));
break;
}
}
}
基本流程:
1:创建一个 AssociationsManager
管理类。
2:拿到一张全局唯一的:AssociationsHashMap
。
3:判断是否插入的关联值value
是否存在,存在走第4步,不存在则会移除关联对象。
4:try_emplace
,并创建一个空的 ObjectAssociationMap
去取查询的键值对:
5:如果发现没有这个 key 就插入一个 空的 BucketT进去并返回true
6:通过setHasAssociatedObjects方法标记对象存在关联对象即置isa指针的has_assoc属性为true
7:用当前 policy 和 value 组成了一个 ObjcAssociation 替换原来 BucketT 中的空
8:标记一下 ObjectAssociationMap 的第一次为 false