寫在前面
首先聲明:題目中所說的“私有方法”只是我們感官上的感覺灵疮,OC 中沒有絕對的私有變量和私有方法演侯。
關(guān)于私有變量和“私有方法”
- 私有變量 用@private來聲明私有變量扭弧,只允許本類訪問伺通。
- “私有方法”OC中沒有提供關(guān)鍵字來聲明私有方法狂秦,可以通過category的匿名類Extension通過在一個(gè)只在類的.m文件中來聲明一個(gè)只能被本類訪問的方法关带。
但是要注意的是:OC是動態(tài)性語言细溅,他的對象類型和真正要調(diào)用的方法是在運(yùn)行時(shí)才確定的褥傍,所以這就決定了在oc中沒有絕對的私有變量和私有方法的,通過runtime我們可以動態(tài)的去對類中所有的變量和方法進(jìn)行操作喇聊,關(guān)于runtime的一些東西在
《iOS-RunTime介紹及使用 http://www.reibang.com/p/5408dcab8f02》中介紹恍风。比如通過class_copyIvarList
和class_copyMethodList
這兩個(gè)方法。
正題
我們在項(xiàng)目中可能會遇到這樣的情況承疲,你引入了一個(gè)庫邻耕,庫里面的.m文件中的方法你不能修改,但是你又必須要用到燕鸽,就像我們遇到的場景兄世,我需要跳到庫里的mainViewController
中,在mainViewController
有個(gè)navigationController
的pop方法啊研,在這個(gè)mainViewController
中定義的pop方法是popToRootViewControllerAnimated
但是我需要的是pop到指定viewController中御滩,但是我們又不能修改mainViewController
這個(gè)文件中的方法,該怎么辦呢党远?這里說一個(gè)利用runtime解決的方法:
替換“私有方法”
在我們當(dāng)前的VC中削解,我們生命一個(gè)方法changeLiveMethod
用來執(zhí)行替換方法操作。
代碼如下:
- 新的pop方法
- (void)popToLiveListVC1
{
UIViewController *target = nil;
for (UIViewController * controller in self.navigationController.viewControllers) { //遍歷
if ([controller isKindOfClass:[LiveListViewController class]]) { //這里判斷是否為你想要跳轉(zhuǎn)的頁面
target = controller;
}
}
if (target)
{
[self.navigationController popToViewController:target animated:YES]; //跳轉(zhuǎn)
}
}
- 替換方法
//當(dāng)前pop方法1
SEL thisSelector = @selector(popToLiveListVC1);
Method thisMethod = class_getInstanceMethod([self class], thisSelector);
//獲取mainViewCOntroller 中的gotoViewController方法
// unsigned int count = 0;
// Method *membFuns = class_copyMethodList([MainViewController class], &count);
NSString *mainPopMethodStr = @"gotoViewController";
// for (int i =0 ; i < count; i++) {
// SEL name = method_getName(membFuns[i]);
// NSString *methodName = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding];
// NSLog(@"m method:%@",methodName);
// if ([methodName isEqualToString:@"gotoViewController"]) {
// mainPopMethodStr = methodName;
// }
// }
SEL mainPopSelector = NSSelectorFromString(mainPopMethodStr);
Method mainPopMethod = class_getInstanceMethod([MainViewController class], mainPopSelector);
method_exchangeImplementations(thisMethod, mainPopMethod);
代碼分析: 先取到當(dāng)前我們需要的新方法 Method沟娱,然后獲取庫里面
MainViewController
私有方法氛驮,我們用Method mainPopMethod = class_getInstanceMethod([MainViewController class], @selector(gotoViewController));
這個(gè)方法編譯器會報(bào)個(gè)警告,所以我們用SEL的這個(gè)方式獲取這個(gè)mainPopMethod
济似,然后進(jìn)行method_exchangeImplementations
方法的交換矫废,這個(gè)方法就是改變類的分發(fā)表盏缤,使它在解析消息的時(shí)候,將一個(gè)選擇器selector對應(yīng)到另一個(gè)替換的實(shí)現(xiàn)蓖扑,并且將該選擇器對應(yīng)的原始實(shí)現(xiàn)關(guān)聯(lián)到新的選擇器中唉铜。
對SEL Method IMP的一些說明
- 選擇器(Selector-typedef struct objc_selector *SEL ):用于在運(yùn)行時(shí)表示一個(gè)方法的名稱,一個(gè)方法選擇器就是一個(gè)C字符串律杠,在運(yùn)行時(shí)會被注冊或者映射潭流,選擇器是由編譯器生成的,并在類被加載的時(shí)候由運(yùn)行時(shí)自動進(jìn)行映射柜去。
- 方法(Method-typedef struct objc_method *Method):在類的定義中代表一個(gè)方法的類型灰嫉。
- 實(shí)現(xiàn)(Implementation- typedef id (*IMP)(id, SEL, ...)):這是一個(gè)指向方法實(shí)現(xiàn)函數(shù)起始地址的指針,這個(gè)函數(shù)的第一個(gè)參數(shù)是指向self的指針诡蜓,第二個(gè)參數(shù)是方法選擇器熬甫,然后是方法的參數(shù)。
理解它們之間關(guān)系的最好方式是:一個(gè)類維護(hù)著一張分發(fā)表(dispatch table)蔓罚,用來解析運(yùn)行時(shí)發(fā)來的消息椿肩;該表的每個(gè)入口是一個(gè)方法(Method),其中的key就是選擇器(SEL)豺谈,對應(yīng)一個(gè)實(shí)現(xiàn)(IMP)郑象,即一個(gè)指向底層c函數(shù)的指針。