本文主要介紹Objectiive-C的Category,當(dāng)你需要擴(kuò)展系統(tǒng)SDK提供的類的時(shí)候,Category就十分有用.Category允許你為已經(jīng)存在的類添加屬性或者方法,供自己使用.
Category添加的屬性和方法是可以供類和其子類使用的.
1.Category 添加實(shí)例方法,類方法和屬性:
@interface UIView (TagID)
-(void)logUIView;
+(void)debugLogUIView;
@property (nonatomic,copy)NSString * tagName;
@end
-(void)setTagName:(NSString *)tagName{
? ? objc_setAssociatedObject(self, "tagName", tagName, OBJC_ASSOCIATION_COPY);
}
-(NSString *)tagName{
? return objc_getAssociatedObject(self, "tagName");
}
-(void)logUIView{
? ? NSLog(@"self-->%@ infomation-->%@",[self class],[self description]);
}
+(void)debugLogUIView{
? ? NSLog(@"self-->%@ info-->%@",self,[self debugDescription]);
}
2.Category 是不能添加實(shí)例變量的,因?yàn)閏ategory是在運(yùn)行時(shí)才會(huì)去添加,此時(shí)對(duì)象的內(nèi)存布局已經(jīng)確定,不能添加實(shí)例變量.
3.Objectiive-C的Category 實(shí)現(xiàn)原理
Runtime源碼中Category的結(jié)構(gòu):
struct category_t {
? ? 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;
? ? // Fields below this point are not always present on disk.
? ? 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);
};
可以看到Category 的結(jié)構(gòu)體中存在名字,類,實(shí)例方法,類方法,協(xié)議,實(shí)例屬性,類屬性,還有Category結(jié)構(gòu)體自帶的方法methodsForMeta(該方法用于返回Categroy的方法).
同時(shí)Runtime會(huì)把Categroy添加到類上
static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
? ? if (!cats) return;
? ? if (PrintReplacedMethods) printReplacements(cls, cats);
//? 獲取當(dāng)前類是不是MetaClass
? ? bool isMeta = cls->isMetaClass();
//為方法列表,和屬性列表,協(xié)議列表開(kāi)辟內(nèi)存空間
? ? // fixme rearrange to remove these intermediate allocations
? ? method_list_t **mlists = (method_list_t **)
? ? ? ? malloc(cats->count * sizeof(*mlists));
? ? property_list_t **proplists = (property_list_t **)
? ? ? ? malloc(cats->count * sizeof(*proplists));
? ? protocol_list_t **protolists = (protocol_list_t **)
? ? ? ? malloc(cats->count * sizeof(*protolists));
//循環(huán)把Categroy加到類上
? ? // Count backwards through cats to get newest categories first
? ? int mcount = 0;
? ? int propcount = 0;
? ? int protocount = 0;
? ? int i = cats->count;
? ? bool fromBundle = NO;
? ? while (i--) {
? ? ? ? auto& entry = cats->list[i];
//? ? ? 獲取Categroy方法列表
? ? ? ? method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
? ? ? ? if (mlist) {
? ? ? ? ? ? mlists[mcount++] = mlist;
? ? ? ? ? ? fromBundle |= entry.hi->isBundle();
? ? ? ? }
//獲取Categroy屬性列表
? ? ? ? property_list_t *proplist =
? ? ? ? ? ? entry.cat->propertiesForMeta(isMeta, entry.hi);
? ? ? ? if (proplist) {
? ? ? ? ? ? proplists[propcount++] = proplist;
? ? ? ? }
//獲取Categroy協(xié)議列表
? ? ? ? protocol_list_t *protolist = entry.cat->protocols;
? ? ? ? if (protolist) {
? ? ? ? ? ? protolists[protocount++] = protolist;
? ? ? ? }
? ? }
//獲取類的符號(hào)結(jié)構(gòu)
? ? auto rw = cls->data();
? ? prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
//? ? 將Catrgroy的方法列表添加到類上(copy到類的方法列表數(shù)組內(nèi)部)
? ? rw->methods.attachLists(mlists, mcount);
? ? free(mlists);
? ? if (flush_caches? &&? mcount > 0) flushCaches(cls);
//將Catgroy的屬性列表添加到類上(copy到類的方法列表數(shù)組內(nèi)部)
? ? rw->properties.attachLists(proplists, propcount);
? ? free(proplists);
//將Catgroy的協(xié)議列表添加到類上(copy到類的方法列表數(shù)組內(nèi)部)
? ? rw->protocols.attachLists(protolists, protocount);
? ? free(protolists);
}
下面這段代碼就是attachLists的實(shí)現(xiàn)
void attachLists(List* const * addedLists, uint32_t addedCount) {
? ? ? ? if (addedCount == 0) return;
? ? ? ? if (hasArray()) {
? ? ? ? ? ? // many lists -> many lists
? ? ? ? ? ? uint32_t oldCount = array()->count;
? ? ? ? ? ? uint32_t newCount = oldCount + addedCount;
? ? ? ? ? ? setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
? ? ? ? ? ? array()->count = newCount;
//? ? ? ? ? ? 將原來(lái)的數(shù)組移動(dòng)到新數(shù)組的addedCount位置之后
? ? ? ? ? ? memmove(array()->lists + addedCount, array()->lists,
? ? ? ? ? ? ? ? ? ? oldCount * sizeof(array()->lists[0]));
//? ? ? ? ? ? 將addedLists copy到新數(shù)組的首位開(kāi)始的位置
? ? ? ? ? ? memcpy(array()->lists, addedLists,
? ? ? ? ? ? ? ? ? addedCount * sizeof(array()->lists[0]));
? ? ? ? }
? ? ? ? else if (!list? &&? addedCount == 1) {
? ? ? ? ? ? // 0 lists -> 1 list
? ? ? ? ? ? list = addedLists[0];
? ? ? ? }
? ? ? ? else {
? ? ? ? ? ? // 1 list -> many lists
? ? ? ? ? ? List* oldList = list;
? ? ? ? ? ? uint32_t oldCount = oldList ? 1 : 0;
? ? ? ? ? ? uint32_t newCount = oldCount + addedCount;
? ? ? ? ? ? setArray((array_t *)malloc(array_t::byteSize(newCount)));
? ? ? ? ? ? array()->count = newCount;
? ? ? ? ? ? if (oldList) array()->lists[addedCount] = oldList;
//? ? ? ? ? ? 將addedLists copy到新數(shù)組的首位開(kāi)始的位置
? ? ? ? ? ? memcpy(array()->lists, addedLists,
? ? ? ? ? ? ? ? ? addedCount * sizeof(array()->lists[0]));
? ? ? ? }
? ? }
category的方法被放到了新方法列表的前面,而原來(lái)類的方法被放到了新方法列表的后面妻熊,這也就是我們平常所說(shuō)的category的方法會(huì)“覆蓋”掉原來(lái)類的同名方法夸浅,這是因?yàn)檫\(yùn)行時(shí)在查找方法的時(shí)候是順著方法列表的順序查找的,它只要一找到對(duì)應(yīng)名字的方法扔役,就會(huì)停止查找.
關(guān)于Categroy的關(guān)聯(lián)對(duì)象
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);
//? ? 如果value存在根據(jù)policy發(fā)送SEL_retain 或SEL_copy
? ? id new_value = value ? acquireValue(value, policy) : nil;
? ? {
//? ? ? ? 獲取到AssociationsManager的hashMap這是儲(chǔ)存所有關(guān)聯(lián)對(duì)象的hashMap
? ? ? ? AssociationsManager manager;
? ? ? ? AssociationsHashMap &associations(manager.associations());
? ? ? ? disguised_ptr_t disguised_object = DISGUISE(object);
? ? ? ? if (new_value) {
? ? ? ? ? ? // break any existing association.
//? ? ? ? ? ? 迭代hashMap并存儲(chǔ)關(guān)聯(lián)的對(duì)象
? ? ? ? ? ? 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);
? ? ? ? ? ? ? ? object->setHasAssociatedObjects();
? ? ? ? ? ? }
? ? ? ? } 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);
}
Runtime采用的是全局的HashMap存儲(chǔ)關(guān)聯(lián)對(duì)象.