Runtime——消息轉(zhuǎn)發(fā)與運(yùn)用

本文主要是針對runtime消息轉(zhuǎn)發(fā)進(jìn)行整理,并舉例關(guān)于消息轉(zhuǎn)發(fā)的運(yùn)用汽馋。

消息轉(zhuǎn)發(fā)

1渣磷、消息調(diào)用

OC中發(fā)送消息是通過objc_msgSend(id, SEL, ...) 來實(shí)現(xiàn)的厉膀,首先會(huì)根據(jù)isa所指向的類結(jié)構(gòu)中進(jìn)行方法查找(objc_method_list),如果該類中無法查找到所對應(yīng)的方法兰吟,則會(huì)沿類結(jié)構(gòu)中的超類指針super_class繼續(xù)向上索引诵冒,直至NSObject類亩进。一旦索引到對應(yīng)方法則會(huì)向該方法傳遞receiver對應(yīng)的數(shù)據(jù)結(jié)構(gòu)皂吮,同時(shí)润文,為了優(yōu)化索引速度,系統(tǒng)會(huì)緩存每次成功索引的方法名和實(shí)現(xiàn)地址到類結(jié)構(gòu)中的objc_cache丘侠。后續(xù)的方法索引將優(yōu)先索引緩存中的方法列表庐扫。

通過isa查找遵湖,流程如下:

if (沒有找到cache缺亮、objc_method_list翁涤,向父類索引至NSObject類) {
則去實(shí)現(xiàn)了動(dòng)態(tài)方法方法解析。
}
else if ( 如果沒有實(shí)現(xiàn)動(dòng)態(tài)方法解析或者解析失敗并且實(shí)現(xiàn)了消息轉(zhuǎn)發(fā)機(jī)制) {
進(jìn)入消息轉(zhuǎn)發(fā)流程
}
else 程序crash

2瞬内、動(dòng)態(tài)方法解析

如果調(diào)用的是實(shí)例方法則會(huì)調(diào)起該方法

+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

如果調(diào)用的是類方法則會(huì)調(diào)起該方法

+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

舉個(gè)??:

+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
    if (aSEL == @selector(resolveThisMethodDynamically)) {
          class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
          return YES;
    }
    return [super resolveInstanceMethod:aSEL];
}
3迷雪、消息轉(zhuǎn)發(fā)

未能向調(diào)用方法提供具體實(shí)現(xiàn)時(shí)即+ (BOOL)resolveInstanceMethod:(SEL)sel;或+ (BOOL)resolveClassMethod:(SEL)sel;返回值為NO限书。此時(shí)將轉(zhuǎn)入消息轉(zhuǎn)發(fā)流程虫蝶。

- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");

這個(gè)方法將是消息轉(zhuǎn)發(fā)的最后機(jī)會(huì),我們可以利用它將原有的消息轉(zhuǎn)發(fā)至另外的對象或者忽略倦西。其中參數(shù)anInvocation是基于面向?qū)ο髮υ蟹椒ㄕ{(diào)用的一層封裝能真,包含了方法名、調(diào)用參數(shù)、方法簽名等粉铐。
舉個(gè)??:

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    if ([someOtherObject respondsToSelector:
            [anInvocation selector]])
        [anInvocation invokeWithTarget:someOtherObject];
    else
        [super forwardInvocation:anInvocation];//避免未處理而導(dǎo)致的Crash
}

運(yùn)用

隨著iOS系統(tǒng)版本的更新疼约,部分性能更優(yōu)異或者可讀性更高的API將有可能對原有API進(jìn)行廢棄與更替。因此在開發(fā)中經(jīng)常需要考慮蝙泼、判斷版本程剥,那是不是可以考慮用runtime來進(jìn)行動(dòng)態(tài)處理?
下面主要是針對適配iOS 11 contentInsetAdjustmentBehaviorautomaticallyAdjustsScrollViewInsets做栗子

1汤踏、新建一個(gè)Category(RTForwarding)

用于調(diào)用到iOS 11屬性contentInsetAdjustmentBehavior的封裝處理
代碼如下:

//重寫runtime方法
//1.為即將轉(zhuǎn)發(fā)的消息返回一個(gè)對應(yīng)的方法簽名(該簽名后面用于對轉(zhuǎn)發(fā)消息對象(NSInvocation *)anInvocation進(jìn)行編碼用)
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { // 1
    NSMethodSignature *signature = nil;
    
    if (aSelector == @selector(setContentInsetAdjustmentBehavior:)) {
        
        signature = [UIViewController instanceMethodSignatureForSelector:@selector(setAutomaticallyAdjustsScrollViewInsets:)];
    }else {
        
        signature = [super methodSignatureForSelector:aSelector];
    }
    
    return signature;
}
//2.開始消息轉(zhuǎn)發(fā)((NSInvocation *)anInvocation封裝了原有消息的調(diào)用织鲸,包括了方法名,方法參數(shù)等)
- (void)forwardInvocation:(NSInvocation *)anInvocation { // 2
    
    BOOL automaticallyAdjustsScrollViewInsets  = NO;
    UIViewController *topmostViewController = [self getTopmostViewController];
    //3.由于轉(zhuǎn)發(fā)調(diào)用的API與原始調(diào)用的API不同溪胶,這里我們新建一個(gè)用于消息調(diào)用的NSInvocation對象viewControllerInvocatio并配置好對應(yīng)的target與selector
    NSInvocation *viewControllerInvocation = [NSInvocation invocationWithMethodSignature:anInvocation.methodSignature]; // 3
    [viewControllerInvocation setTarget:topmostViewController];
    [viewControllerInvocation setSelector:@selector(setAutomaticallyAdjustsScrollViewInsets:)];
    //4.配置所需參數(shù):由于每個(gè)方法實(shí)際是默認(rèn)自帶兩個(gè)參數(shù)的:self和_cmd搂擦,所以我們要配置其他參數(shù)時(shí)是從第三個(gè)參數(shù)開始配置
    [viewControllerInvocation setArgument:&automaticallyAdjustsScrollViewInsets atIndex:2]; // 4
    //5.消息轉(zhuǎn)發(fā)
    [viewControllerInvocation invokeWithTarget:topmostViewController]; // 5
}

//獲取棧頂控制器
- (UIViewController *)getTopmostViewController {
    
    UIViewController *resultVC;
    resultVC = [self getTopmostViewController:[[UIApplication sharedApplication].keyWindow rootViewController]];
    while (resultVC.presentedViewController) {
        resultVC = [self getTopmostViewController:resultVC.presentedViewController];
    }
    return resultVC;
}
- (UIViewController *)getTopmostViewController:(UIViewController *)vc {
    if ([vc isKindOfClass:[UINavigationController class]]) {
        return [self getTopmostViewController:[(UINavigationController *)vc topViewController]];
    } else if ([vc isKindOfClass:[UITabBarController class]]) {
        return [self getTopmostViewController:[(UITabBarController *)vc selectedViewController]];
    } else {
        return vc;
    }
}
2、調(diào)用Category(RTForwarding)

在需要使用的地方導(dǎo)入頭文件

#import "UIScrollView+RTForwarding.h"

在使用到iOS 11屬性contentInsetAdjustmentBehavior時(shí)哗脖,不需要進(jìn)行判斷就可以實(shí)現(xiàn)之前需要判斷功能瀑踢。
代碼如下:

    CGSize main = [UIScreen mainScreen].bounds.size;
    
    UITableView * tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 64, main.width, main.height - 64)];
    tableView.delegate = self;
    tableView.dataSource = self;
    tableView.backgroundColor = [UIColor orangeColor];
    tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;//無需判斷,簡單粗暴
    [self.view addSubview:tableView];

理解了消息轉(zhuǎn)發(fā)才避,在應(yīng)用上還是能夠出其不意達(dá)到簡單粗暴的效果橱夭。本文主要是參考來源與鏈接,該文作者寫的賊棒桑逝,本文主要是對自己的理解進(jìn)行歸納整理徘钥,讓自己的思路更清晰,當(dāng)然如果有寫的不對的地方歡迎指出肢娘。最后呈础,當(dāng)然也是最重要的附上demo地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末橱健,一起剝皮案震驚了整個(gè)濱河市而钞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拘荡,老刑警劉巖臼节,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異珊皿,居然都是意外死亡网缝,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進(jìn)店門蟋定,熙熙樓的掌柜王于貴愁眉苦臉地迎上來粉臊,“玉大人,你說我怎么就攤上這事驶兜《笾伲” “怎么了远寸?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長屠凶。 經(jīng)常有香客問我驰后,道長,這世上最難降的妖魔是什么矗愧? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任灶芝,我火速辦了婚禮,結(jié)果婚禮上唉韭,老公的妹妹穿的比我還像新娘监署。我一直安慰自己,他們只是感情好纽哥,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布钠乏。 她就那樣靜靜地躺著,像睡著了一般春塌。 火紅的嫁衣襯著肌膚如雪晓避。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天只壳,我揣著相機(jī)與錄音俏拱,去河邊找鬼。 笑死吼句,一個(gè)胖子當(dāng)著我的面吹牛锅必,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播惕艳,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼搞隐,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了远搪?” 一聲冷哼從身側(cè)響起劣纲,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谁鳍,沒想到半個(gè)月后癞季,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡倘潜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年绷柒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涮因。...
    茶點(diǎn)故事閱讀 39,992評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡废睦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蕊退,到底是詐尸還是另有隱情郊楣,我是刑警寧澤,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布瓤荔,位于F島的核電站净蚤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏输硝。R本人自食惡果不足惜今瀑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望点把。 院中可真熱鬧橘荠,春花似錦、人聲如沸郎逃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽褒翰。三九已至贮懈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間优训,已是汗流浹背朵你。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留揣非,地道東北人抡医。 一個(gè)月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像早敬,于是被迫代替她去往敵國和親忌傻。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評論 2 355