Objective-C 奇巧淫技--底層強行調用高層介面,objective-c高層
奇技淫巧 指過於奇巧而無益的技藝與製品.
先來看一段代碼
// A.h- (void)funa{ [b funB];} // b.h-(void)funB{ // 我在這裡如何得到調用對象b的對象a呢}
有的時候底層就是想知道高層,別糾結什麼時候,這個主題下的文章註定都是這個德行的.我們又不想把a傳給b.那麼有辦法的到a麼,答案肯定是有的.
方法在調用的過程中呢,會有個壓棧的過程,oc下棧裡的東西按照高位到地位排序為:
1 self對象 2 方法sel 3 參數… 4變數…
在方法funb構造一個變數,然後地址 加加 就獲得了方法funB了,再 加加 就獲得b對象了
-(void)funB{ int a = 999; int *obj = &a + 2; SEL *sel = &a + 1; NSLog(@"%s, a %p", __FUNCTION__, &a); NSLog(@"%s, obj %p", __FUNCTION__, obj); NSLog(@"%s, sel %p", __FUNCTION__, sel); NSLog(@"%@", *obj); NSString *str2 = NSStringFromSelector(*sel); NSLog(@"%@", str2);}
這個時候如果我們在 加加 會發生什麼事情呢?當我們在 加加加 的時候,我們就進入到方法funa用到的棧的地址了,我們可以得到funa裡的局部變數,這種情況下再 加加 就可以得到a了.整理下思路就是:
1 擷取堆棧資訊
2 將funa當作錨點
3 funa的地址 加加 就是對象a
// a.h-(void)funA{ NSLog(@"%s", __FUNCTION__);}- (IBAction)click2:(id)sender { [_test funB];}// b.h-(void)funB{ int a = 999; id b = @"bbbb"; NSString *name = [CallStack preSelName]; // NSLog(@"%@", name); SEL *preSel = &a + 1; int i = 0; SEL *sel = &a + 1; NSString *str2 = NSStringFromSelector(*sel); NSString *selName = NSStringFromSelector(*preSel); while (![name isEqualToString:selName]) { i++; preSel = &a + i; selName = NSStringFromSelector(*preSel); if (selName) { // NSLog(@"preSel: %p %@", *preSel, selName); } } // NSLog(@"%@", selName); int *preClazz = *(preSel + 1); // NSLog(@"%@", preClazz); id objc= (__bridge id)((void *)(preClazz)); NSLog(@"%@", objc); [objc performSelector:@selector(funA) withObject:nil];}
啟動並執行結果是
2015-03-24 21:13:51.629 testaddress[12616:2623719] -[FirstViewController funA]
底層的b在不知道高層a的情況下,調用a的方法funA.
demo請看
https://github.com/uxyheaven/demo_testaddress
裡的Button2