一、分類及擴展
分類又名 Category祖凫、類別
- 分類中原則上只能添加方法型豁。不能增加成員變量。
- 分類中可以訪問原來類中的成員變量互纯,但是只能訪問@protect和@public形式的變量
- 分類中可以用@property 聲明變量瑟幕,只會生成變量的setter、getter方法的聲明,不能生成方法實現(xiàn) 和 帶下劃線的成員變量
- 可以通過runtime 給分類添加屬性只盹,即屬性關聯(lián)辣往,重寫setter、getter方法
- 如果分類中有和原有類同名的方法殖卑,會優(yōu)先調(diào)用分類中的方法站削,就是說會忽略原有類的方法。
- 如果多個分類中都有同名方法 那么他的調(diào)用順序和編譯順序有關系
可以在 bulid Phases -> Compile Sources中查看順序 由下而上調(diào)用 - 如果繼承關系 子類 和父類 和父類分類擁有同名方法 子類調(diào)用 父類和父類分類(不管父類有無實現(xiàn))均不會響應孵稽。 如子類并未實現(xiàn) 父類 和父類分類 有同名方法 子類發(fā)起調(diào)用 則會 調(diào)用父類分類方法 父類分類方法(不管父類有無實現(xiàn))父類分類調(diào)用順序依舊和編譯順序有關
類擴展又名 Extension
- 類擴展不僅可以增加方法许起, 可以增加成員變量及屬性,只是全部為私有的菩鲜。
- 可以說成特殊的分類园细,也可稱之為 匿名分類。
類擴展探究
類擴展創(chuàng)建方式
-
直接在類中寫接校,向下面我們熟悉的味道,永遠在實現(xiàn)之前聲明之后
截屏2020-10-27 下午2.44.19.png -
手動創(chuàng)建一個.h文件單拎出來猛频。通過 command+N 新建 -> Objective-C File -> 選擇Extension截屏2020-10-27 下午2.47.05.png
類擴展底層原理探索
寫一個類擴展并通過clang查看其源碼
clang -rewrite-objc main.mm -o main.cpp
截屏2020-10-27 下午3.06.08.png
截屏2020-10-27 下午3.10.14.png
截屏2020-10-27 下午3.15.26.png
查看 LGTeacher 類拓展的方法,在編譯過程中蛛勉,方法就直接添加到了 methodlist中伦乔,作為類的一部分,即編譯時期直接添加到本類里面
-
運行objc源碼程序 在readClass寫上針對性LGTeacher的代碼
截屏2020-10-27 下午3.30.56.png
總結(jié)
- 由此可以看出 類擴展 在編譯期 就會作為類的一部分董习,和類一起編譯進來
- 類擴展只是聲明烈和,且需要依賴當前的主類。
分類關聯(lián)對象底層探究
截屏2020-10-27 下午3.51.40.png
-
objc_setAssociatedObject源碼實現(xiàn)
截屏2020-10-27 下午4.04.33.png - _object_set_associative_reference
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));
/// 將object 偽裝成 DisguisedPtr 類型數(shù)據(jù)結(jié)構(gòu)
DisguisedPtr<objc_object> disguised{(objc_object *)object};
/// 將策略 和 value 存入 ObjcAssociation 數(shù)據(jù)結(jié)構(gòu)中
ObjcAssociation association{policy, value};
// retain the new value (if any) outside the lock.
association.acquireValue();
/// 局部作用域
{
///初始化一個關聯(lián) 管理對象 在構(gòu)造時加鎖皿淋,析構(gòu)時開鎖
AssociationsManager manager;
/// AssociationsHashMap 類型 就是一個嵌套的DenseMap
///DenseMap<DisguisedPtr<objc_object>, DenseMap<const void *, ObjcAssociation>>
/// manager.get() :
/// 獲取的是一個 static的_mapStorage變量 associations數(shù)據(jù)為manager.get()
///獲取而來 所以associations是全局靜態(tài)變量
AssociationsHashMap &associations(manager.get());
if (value) {
// try_emplace: 在這disguised作為key查找招刹,如果已經(jīng)在associations表中,就把查
/// 找到的桶作為DenseMapIterator的位置指針進行初始化窝趣,然后用pair包裝后返回疯暑;key
///沒在associations表中就把disguised作為key,ObjectAssociationMap{}作為value存入
///桶中哑舒,然后把該桶作為DenseMapIterator的位置指針進行初始化妇拯,然后用pair包裝后
///返回。返回值類型std::pair<DenseMapIterator, bool>
auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
if (refs_result.second) {
//能進來這里洗鸵,說明disguised為key的桶是新插入進來的越锈,所以根據(jù)條件設置isa_t中的has_assoc位為true
object->setHasAssociatedObjects();
}
//找到associations中的disguised對應的ObjectAssociationMap表
auto &refs = refs_result.first->second;
//用key在ObjectAssociationMap表中查找,如果表中不存在該key那么就把key和association對應插入到ObjectAssociationMap中
auto result = refs.try_emplace(key, std::move(association));
//result.second為false, 說明ObjectAssociationMap表中原來已有該key膘滨,不會移動甘凭,所以這里進行了swap的操作來交換association的值。
if (!result.second) {
association.swap(result.first->second);
}
} else { //value為nil, 取消關聯(lián)火邓。
//先從associations表中找到disguised對應的ObjectAssociationMap表丹弱,用pair包裝后返回德撬。
auto refs_it = associations.find(disguised);
//如果從associations表中找到了disguised對應的ObjectAssociationMap表
if (refs_it != associations.end()) {
//從pair中拿到ObjectAssociationMap表
auto &refs = refs_it->second;
//從ObjectAssociationMap表中查找key對應的association,然后把它作為DenseMapIterator的位置指針初始化后返回
auto it = refs.find(key);
//如果找到了就進去
if (it != refs.end()) {
//這里交換值是為了把要擦除的association記錄下來躲胳,因為下面還要進行releaseHeldValue
association.swap(it->second);
//從ObjectAssociationMap表中擦除association以及其他相應的操作
refs.erase(it);
if (refs.size() == 0) {
//說明沒有關聯(lián)的值了蜓洪,從associations表中擦除ObjectAssociationMap表
associations.erase(refs_it);
}
}
}
}
}
// release the old value (outside of the lock).//釋放
association.releaseHeldValue();
}
- AssociationsManager源碼結(jié)構(gòu)
// class AssociationsManager manages a lock / hash table singleton pair.
// Allocating an instance acquires the lock
class AssociationsManager {
using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>;
static Storage _mapStorage;
public:
AssociationsManager() { AssociationsManagerLock.lock(); }///初始化 加鎖
~AssociationsManager() { AssociationsManagerLock.unlock(); }///析構(gòu) 解鎖
AssociationsHashMap &get() {
return _mapStorage.get();
}
static void init() {
_mapStorage.init();
}
}
從中可以看出get() 方法獲取的為全局靜態(tài)變量 唯一的
- AssociationsHashMap
&associations
() 源碼結(jié)構(gòu)
class ObjcAssociation {
uintptr_t _policy;
id _value;
.....省略
}
typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap;
typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;
截屏2020-10-27 下午10.25.39.png
- try_emplace
2251862-56319ac00d5108f6.jpg
- iterator
截屏2020-10-27 下午11.14.44.png
截屏2020-10-27 下午11.16.39.png
從這里可以看出 返回值 pair<iterator,bool>類型 可以拆解為
pair<KeyT
,ValueT
,ValueInfoT
,KeyInfoT
,BucketT
,false
,Bool
>
pair<KeyT
,ValueT
,DenseMapValueInfo<ValueT>
,DenseMapInfo<KeyT>
,detail::DenseMapPair<KeyT, ValueT>
,false
,Bool
>
截屏2020-10-27 下午10.59.33.png
優(yōu)化打印值
///1.KeyT
//pair<objc::DenseMapIterator<DisguisedPtr<objc_object>,
///2.ValueT
//objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >,
///3.DenseMapValueInfo<ValueT> -> ValueInfoT
//objc::DenseMapValueInfo<objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >,
///4.DenseMapInfo<KeyT> -> KeyInfoT
//objc::DenseMapInfo<DisguisedPtr<objc_object> >,
///5.detail::DenseMapPair<KeyT, ValueT> -> BucketT
//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> > >,
///6.false
//false>,
///7.bool
//bool>
到這里可以得出下面的一張關系圖
關聯(lián)對象結(jié)構(gòu) 2.jpg
objc_getAssociatedObject源碼分析
id objc_getAssociatedObject(id object, const void *key)
{
return _object_get_associative_reference(object, key);
}
id _object_get_associative_reference(id object, const void *key)
{
//先初始化一個用來接收值的association
ObjcAssociation association{};
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
//用object作為key從associations表中找到對應的ObjectAssociationMap表
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
if (i != associations.end()) {
ObjectAssociationMap &refs = i->second;
//用key在ObjectAssociationMap表中搜索對應的ObjcAssociation
ObjectAssociationMap::iterator j = refs.find(key);
if (j != refs.end()) {
//找到后賦值給association,然后retain
association = j->second;
association.retainReturnedValue();
}
}
}
return association.autoreleaseReturnedValue();
}
關聯(lián)對象釋放
通過_objc_rootDealloc
->rootDealloc
->object_dispose
->objc_destructInstance
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
//如果有關聯(lián)對象坯苹,移除
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
void _object_remove_assocations(id object)
{
ObjectAssociationMap refs{};
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
if (i != associations.end()) {
refs.swap(i->second);
associations.erase(i);
}
}
// release everything (outside of the lock).
for (auto &i: refs) {
i.second.releaseHeldValue();
}
}
從中可以看出關聯(lián)對象不用手動移除隆檀,在對象釋放時會自動移除。
總結(jié)
- 類擴展是在編譯期就已經(jīng)是類的一部分了北滥。一般用于對外隱藏屬性和方法刚操,但并不是真正的私有。
- 關聯(lián)對象通過manager維護了以偽裝后的objc_object指針為key的AssociationsHashMap表和以const void *類型的指針為key的ObjectAssociationMap表再芋。ObjectAssociation就存在ObjectAssociationMap表中菊霜。
- 關聯(lián)對象在對象釋放時會自動移除。