類的擴展和分類
-
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