我們平時(shí)在使用三方時(shí)會(huì)比較喜歡根據(jù)自己的意愿去封裝一下三方,一般都會(huì)繼承三方創(chuàng)建一些子類矫限,用于對(duì)應(yīng)不同的需求法绵。有時(shí)三方會(huì)給我們留出接口供我們使用凭戴,但有時(shí)候并沒有涧狮。如果我們?nèi)ソo三方直接擴(kuò)充這些接口是不友好的,特別是CocoaPods上的三方么夫,所以一般我們會(huì)在子類里重寫三方的方法者冤。
三方中在.h中聲明的方法比較好重寫,直接在子類里重寫方法档痪,如果需要重調(diào)父類原有方法可以用super直接調(diào)用涉枫。而一些私有方法并沒有在.h中聲明,這時(shí)我們也可以直接重寫父類方法钞它,但是相對(duì)于想要重調(diào)父類方法,就不能用super直接調(diào)用來(lái)實(shí)現(xiàn)。這里super只是一個(gè)編譯器修飾符遭垛,是一個(gè)指向父類標(biāo)志尼桶,并不是對(duì)象的父類實(shí)例。所以你再用super調(diào)用父類的私有方法是沒有作用的锯仪。這時(shí)我們有幾種選擇可以幫我們實(shí)現(xiàn)父類私有方法的調(diào)用泵督。
首先可以用Method Swizzling(方法交換)來(lái)幫我們實(shí)現(xiàn),但是我并不喜歡使用這個(gè)方法庶喜,因?yàn)榇朔椒〞?huì)引起全局的方法指針交換小腊,多人開發(fā)中如果沒有交流好很容易出現(xiàn)一些紕漏等問(wèn)題,所以這里我就不具體介紹此方法了久窟。
第二種方法是我們可以利用runtime的消息發(fā)送機(jī)制秩冈,為我們的對(duì)象調(diào)用其父類的私有方法。主要用到objc_msgSendSuper方法斥扛,但是直接調(diào)用是不安全的入问,因?yàn)槟悴荒艽_定父類是否含有這個(gè)方法,因?yàn)槿揭部赡苓M(jìn)行升級(jí)等操作稀颁,所以調(diào)用之前需要判斷父類是否含有此方法芬失,舉個(gè)例子,代碼如下:
// 判斷一個(gè)父類是否包含某個(gè)方法(包含私有方法)
- (BOOL)lj_containsSuperMethod:(NSString *)methodName {
unsigned int outCount = 0;
Method *methods = class_copyMethodList([SuperClass class], &outCount);
for (int i = 0; i < outCount; i ++) {
Method method = methods[i];
SEL methodNameSEL = method_getName(method);
if ([methodName isEqualToString:NSStringFromSelector(methodNameSEL)]) {
free(methods);
return YES;
}
}
free(methods);
return NO;
}
這個(gè)方法是利用遍歷父類中的所有方法來(lái)判斷是否包含某一方法匾灶,這樣做其實(shí)也有一定的弊端棱烂,比如當(dāng)這個(gè)父類方法很多時(shí),而你又需要頻繁調(diào)用此方法時(shí)就會(huì)引起不必要的消耗阶女。如果父類包含此方法就直接利用objc_msgSendSuper發(fā)送消息就可以了颊糜,需要聲明#import <objc/message.h>。例子代碼如下:
- (void)privateMethod {
if ([self lj_containsSuperMethod:@"privateMethod"]) {
struct objc_super super_obj;
super_obj.receiver = self;
super_obj.super_class = [SuperClass class];
objc_msgSendSuper(&super_obj, sel_registerName("privateMethod"));
}
}
這里調(diào)用objc_msgSendSuper方法需要將ENABLE_STRICT_OBJC_MSGSEND設(shè)置為NO张肾。
第三種是先獲取父類方法芭析,然后利用構(gòu)建的方式來(lái)調(diào)用。判斷父類是否包含此方法是根據(jù)構(gòu)建出來(lái)的方法是否存在來(lái)判斷的吞瞪,相比于上一個(gè)方法減少了遍歷的過(guò)程馁启。例子代碼如下:
- (void)privateMethod {
Method method = class_getInstanceMethod([SuperClass class], sel_registerName("privateMethod"));
void (*super_func)(id,SEL) = (void *)method_getImplementation(method);
if (super_func) super_func(self, sel_registerName("privateMethod"));
}
if (super_func) 是判斷父類是否包含的此方法,如果含有此方法則調(diào)用芍秆,否則不調(diào)用惯疙。