好久沒寫blog了吗冤,有時(shí)候?qū)幵付嗷〞r(shí)間去研究新東西问顷,也懶得寫。最近在寫category的時(shí)候發(fā)現(xiàn)了很多有意思的東西冗荸,想在這里跟大家分享一下承璃。有時(shí)候我們在創(chuàng)建一個(gè)類別后,重寫了原類的方法俏竞,這個(gè)并不會(huì)報(bào)錯(cuò)绸硕,但是會(huì)有警告“Category is implementing a method which will also be implemented by its primary class”堂竟,運(yùn)行起來也可以執(zhí)行,但是不會(huì)執(zhí)行原類的方法玻佩,很多人認(rèn)為覆蓋執(zhí)行了出嘹。
其實(shí)不然,category只是添加了一個(gè)新的方法咬崔,在執(zhí)行的過程中税稼,取的是方法列表的第一個(gè)方法就沒有再去執(zhí)行其他相同名稱的方法了。我們看源碼:
for (uint32_t m = 0;
(scanForCustomRR || scanForCustomAWZ) && m < mlist->count;
m++)
{
SEL sel = method_list_nth(mlist, m)->name;
if (scanForCustomRR && isRRSelector(sel)) {
cls->setHasCustomRR();
scanForCustomRR = false;
} else if (scanForCustomAWZ && isAWZSelector(sel)) {
cls->setHasCustomAWZ();
scanForCustomAWZ = false;
}
}
// Fill method list array
newLists[newCount++] = mlist;
.
.
.
// Copy old methods to the method list array
for (i = 0; i < oldCount; i++) {
newLists[newCount++] = oldLists[i];
}
源碼中category新添加的方法是放在newLists前面的垮斯,其他方法在后面郎仆,我們總結(jié)一下:
1)、category的方法沒有“替換掉”原類的方法兜蠕,也就是說如果category和原來類都有methodA扰肌,那么category附加完成之后,類的方法列表里會(huì)有兩個(gè)methodA
2)熊杨、category的方法被放到了新方法列表的前面曙旭,而原來類的方法被放到了新方法列表的后面,這也就是我們平常所說的category的方法會(huì)“覆蓋”掉原來類的同名方法晶府,這是因?yàn)檫\(yùn)行時(shí)在查找方法的時(shí)候是順著方法列表的順序查找的桂躏,它只要一找到對應(yīng)名字的方法,就不會(huì)繼續(xù)查找了川陆,但是后面可能還有一樣名字的方法剂习。
所以原類的方法我們還是可以找到的,一般存在于方法列表最后较沪,我們是可以取出來的
if (currentClass) {
unsigned int methodCount;
Method *methodList = class_copyMethodList(currentClass, &methodCount);
IMP lastImp = NULL;
SEL lastSel = NULL;
for (NSInteger i = 0; i < methodCount; i++) {
Method method = methodList[i];
NSString *methodName = [NSString stringWithCString:sel_getName(method_getName(method))
encoding:NSUTF8StringEncoding];
if ([@"targetName" isEqualToString:methodName]) {
lastImp = method_getImplementation(method);
lastSel = method_getName(method);
}
}
typedef void (*fn)(id,SEL);
if (lastImp != NULL) {
fn f = (fn)lastImp;
f(my,lastSel);
}
free(methodList);
}
利用這個(gè)原理鳞绕,我們可以Hook原方法之后,找出原方法再執(zhí)行购对。一般我們通過Swizzle去實(shí)現(xiàn)猾昆,現(xiàn)在呢陶因,又多了一種Hook的方法了骡苞。
這種“覆蓋”原方法的編碼方式蘋果本身并不建議,但是如果我們自己把握好節(jié)奏也是可以的楷扬。我個(gè)人也并不建議這樣編寫解幽,因?yàn)楫?dāng)多人同時(shí)編寫代碼時(shí),如果每個(gè)人都寫了自己的一套category并且覆蓋了同樣的方法烘苹,編譯并不會(huì)報(bào)錯(cuò)躲株,那么到底執(zhí)行誰寫的代碼呢,就會(huì)出現(xiàn)執(zhí)行混亂镣衡。所以并不建議使用類別“覆蓋”原方法霜定。如果想要真正覆蓋原方法档悠,可以使用繼承來實(shí)現(xiàn)。
我們再來看另一個(gè)問題望浩,我現(xiàn)在創(chuàng)建了TestMethod和TestMethod2兩個(gè)category辖所,如圖:
@implementation StrategyDemo (TestMethod)
- (void)printMethod
{
NSLog(@"printMethod 1");
}
@implementation StrategyDemo (TestMethod2)
- (void)printMethod
{
NSLog(@"printMethod 2");
}
并同時(shí)添加了printMethod的方法,然后我調(diào)用printMethod后到底是打印categoryA的方法還是打印categoryB的方法呢磨德?還是都會(huì)打印呢缘回?
我們上面講過,category只是添加了一個(gè)新的方法,并沒有覆蓋原方法典挑,只是在執(zhí)行的時(shí)候是取的methodList里最靠前的執(zhí)行酥宴。所以打印結(jié)果一定是編譯時(shí)methodList最先匹配到方法名的方法。我們也可以手動(dòng)調(diào)整編譯順序您觉,進(jìn)而改變methodList中方法的順序拙寡,最后改變執(zhí)行的方法。
上圖是調(diào)整前的順序琳水,當(dāng)我調(diào)整后:
再看打印結(jié)果:
調(diào)整后確實(shí)是改變了編譯時(shí)methodlist里方法的順序倒庵。