類的擴(kuò)展和分類
-
category:分類、類別
- 給類增加方法
- 不能添加成員變量
- 可以使用
runtime給分類添加屬性 - 分類中添加的屬性指揮生成對(duì)應(yīng)的
setter、getter方法的聲明,不能生成方法實(shí)現(xiàn)和帶下劃線的成員變量。
-
extension:類擴(kuò)展
- 可以添加成員屬性,屬于私有。
- 可以添加方法,屬于私有。
關(guān)于類的擴(kuò)展,是放在類的聲明之后類的實(shí)現(xiàn)之前。類的擴(kuò)展里面放的多是私有方法和成員變量。類的擴(kuò)展會(huì)在編譯期就作為類的一部分寫入到類信息中。另外,類的擴(kuò)展只是一個(gè)聲明,它依賴主類需要在主類內(nèi)部完成實(shí)現(xiàn)。
關(guān)聯(lián)對(duì)象
分類不能添加屬性,它只是聲明了setter、getter方法,沒(méi)有實(shí)現(xiàn),那么就需要借助關(guān)聯(lián)對(duì)象來(lái)實(shí)現(xiàn)這個(gè)功能。
分類.h中
@property (nonatomic, copy) NSString *name;
分類.m中
- (void)setCate_name:(NSString *)name{
/**
(對(duì)象,標(biāo)識(shí)符,value, 策略)
*/
objc_setAssociatedObject(self, "name", cate_name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name{
return objc_getAssociatedObject(self, "name");
}
關(guān)聯(lián)對(duì)象源碼探索
關(guān)聯(lián)對(duì)象如何運(yùn)作的
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);
}
關(guān)聯(lián)對(duì)象設(shè)值底層調(diào)用的方法就是_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();
}
傳遞進(jìn)來(lái)的object被包裝成了DisguisedPtr,對(duì)應(yīng)的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()更具傳遞進(jìn)來(lái)的polic對(duì)值進(jìn)行處理
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:創(chuàng)建一個(gè) AssociationsManager 管理類。
2:拿到一張全局唯一的:AssociationsHashMap。
3:判斷是否插入的關(guān)聯(lián)值value是否存在,存在走第4步,不存在則會(huì)移除關(guān)聯(lián)對(duì)象。
4:try_emplace,并創(chuàng)建一個(gè)空的 ObjectAssociationMap 去取查詢的鍵值對(duì):
5:如果發(fā)現(xiàn)沒(méi)有這個(gè) key 就插入一個(gè) 空的 BucketT進(jìn)去并返回true
6:通過(guò)setHasAssociatedObjects方法標(biāo)記對(duì)象存在關(guān)聯(lián)對(duì)象即置isa指針的has_assoc屬性為true
7:用當(dāng)前 policy 和 value 組成了一個(gè) ObjcAssociation 替換原來(lái) BucketT 中的空
8:標(biāo)記一下 ObjectAssociationMap 的第一次為 false