分類(lèi)
1.分類(lèi)數(shù)據(jù)結(jié)構(gòu)
struct objc_category {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
struct property_list_t *_classProperties;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta , struct header_info *hi);
}
2.分類(lèi)添加方法的原理:
- 編譯期過(guò)程
- 編譯器生成實(shí)例方法列表OBJC$CATEGORY_INSTANCE_METHODS"className"$"categoryName"和屬性列表OBJC$PROP_LIST"className"$"categoryName"曹傀;用以填充我們?cè)陬?lèi)中定義的方法跟屬性墓塌。
- 編譯器生成category本身,并用前面生成的方法列表跟屬性列表初始化category本身
- 編譯器在DATA段下的objc_catlist section里保存了一個(gè)大小為1的category_t的數(shù)組L_OBJC_LABELCATEGORY$,用于運(yùn)行期的加載.(關(guān)于DATA段以及以下dyld的內(nèi)容鬼譬,會(huì)在mach-O的篇章介紹)
- 運(yùn)行期過(guò)程
- 拿到catlist上的category_t數(shù)組,把category_t上的實(shí)例方法谚鄙,協(xié)議以及屬性添加到類(lèi)上
- 把category_t的類(lèi)方法和協(xié)議添加到類(lèi)的metaclass上
- 運(yùn)行期添加分類(lèi)過(guò)程
- addUnattachedCategoryForClass將類(lèi)和category做一個(gè)映射
- remethodizeClass負(fù)責(zé)處理添加事宜
- 添加實(shí)例方法會(huì)調(diào)用attachCategoryMethods,它將所有category的實(shí)例方法列表拼成一個(gè)大的實(shí)例方法列表较沪,然后轉(zhuǎn)交給attachMethodList
3.添加屬性
我們都知道無(wú)法給分類(lèi)添加成員變量宣增,因?yàn)閏ategory并不會(huì)為我們生成set,get方法浊吏,這個(gè)時(shí)候可以用關(guān)聯(lián)對(duì)象來(lái)實(shí)現(xiàn)
objc_setAssociatedObject();
objc_getAssociatedObject();
關(guān)聯(lián)對(duì)象的原理分析,內(nèi)存管理可看以下章節(jié)
4.load方法與intialize方法的比較
5.補(bǔ)充的點(diǎn)
- 分類(lèi)的方法只會(huì)添加救氯,不會(huì)覆蓋原先的方法找田,調(diào)用同名方法時(shí)調(diào)用的是最后編譯的分類(lèi)文件中的方法實(shí)現(xiàn)。
- intialize方法會(huì)先調(diào)用父類(lèi)着憨,再子類(lèi)墩衙,所以子類(lèi)未實(shí)現(xiàn)時(shí),父類(lèi)會(huì)被多次調(diào)用甲抖。
類(lèi)擴(kuò)展與分類(lèi)區(qū)別
- 擴(kuò)展可以當(dāng)成一個(gè)匿名的類(lèi)漆改,但是必須要有該類(lèi)的源碼的時(shí)候才能編寫(xiě)擴(kuò)展。(擴(kuò)展不能擁有獨(dú)立的實(shí)現(xiàn)部分)
- 類(lèi)擴(kuò)展是在編譯期添加到類(lèi)中准谚,而分類(lèi)是在運(yùn)行時(shí)添加
- 寫(xiě)在.m的類(lèi)擴(kuò)展方法與屬性是私有的
關(guān)聯(lián)對(duì)象
- 關(guān)聯(lián)對(duì)象保存在什么地方
我們先看看函數(shù)_object_set_associative_reference
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// retain the new value (if any) outside the lock.
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// secondary table exists
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);
} else {
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
// create the new association (first time).
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
_class_setInstancesHaveAssociatedObjects(_object_getClass(object));
}
} else {
// setting the association to nil breaks the association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
refs->erase(j);
}
}
}
}
// release the old value (outside of the lock).
if (old_association.hasValue()) ReleaseValue()(old_association);
}
從以上代碼可知關(guān)聯(lián)對(duì)象由AssociationsManager管理挫剑,定義如下
class AssociationsManager {
static OSSpinLock _lock;
static AssociationsHashMap *_map; // associative references: object pointer -> PtrPtrHashMap.
public:
AssociationsManager() { OSSpinLockLock(&_lock); }
~AssociationsManager() { OSSpinLockUnlock(&_lock); }
AssociationsHashMap &associations() {
if (_map == NULL)
_map = new AssociationsHashMap();
return *_map;
}
};
AssociationsManager里面是由一個(gè)靜態(tài)AssociationsHashMap來(lái)存儲(chǔ)所有的關(guān)聯(lián)對(duì)象的。這相當(dāng)于把所有對(duì)象的關(guān)聯(lián)對(duì)象都存在一個(gè)全局map里面柱衔。而map的的key是這個(gè)對(duì)象的指針地址(任意兩個(gè)不同對(duì)象的指針地址一定是不同的)樊破,而這個(gè)map的value又是另外一個(gè)AssociationsHashMap,里面保存了關(guān)聯(lián)對(duì)象的kv對(duì)秀存。
- 關(guān)聯(lián)對(duì)象的內(nèi)存管理
void *objc_destructInstance(id obj)
{
if (obj) {
Class isa_gen = _object_getClass(obj);
class_t *isa = newcls(isa_gen);
// Read all of the flags at once for performance.
bool cxx = hasCxxStructors(isa);
bool assoc = !UseGC && _class_instancesHaveAssociatedObjects(isa_gen);
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
if (!UseGC) objc_clear_deallocating(obj);
}
return obj;
}
runtime的銷(xiāo)毀對(duì)象函數(shù)objc_destructInstance里面會(huì)判斷這個(gè)對(duì)象有沒(méi)有關(guān)聯(lián)對(duì)象捶码,如果有,會(huì)調(diào)用_object_remove_assocations做關(guān)聯(lián)對(duì)象的清理工作或链。