iOS訊息轉寄學習筆記

來源:互聯網
上載者:User

標籤:tar   targe   method   建立   ng2   end   type   nbsp   ret   

如果深入學習ios Runtime,不得不提到訊息轉寄,很多架構的實現都基於這一功能實現(例如JSPatch)

雖然看了很多篇關於訊息轉寄的文章,但是理解的不是很透徹,還是自己實踐一些理解能更加透徹一下。

首先我自己定義了一個MyString繼承NSString

@interface MyString : NSString@end@implementation MyString@end

然後建立一個MyString,通過performSelector調用MissMethod,MissMethod1,MissMethod2等方法。

- (void)testForward{    MyString *str = [[MyString alloc] init];    [str performSelector:@selector(MissMethod) withObject:nil];    [str performSelector:@selector(MissMethod2) withObject:nil];    [str performSelector:@selector(MissMethod3) withObject:nil];}

如果什麼都不寫,這樣肯定會crash,會出現這個錯誤[NSObject(NSObject) doesNotRecognizeSelector:],因為MyString沒有這三個方法,而且父類也沒有。

如果沒有找到方法,系統會嘗試進行補救,看看有沒有能處理的能力,首先會調用resolveInstanceMethod這個方法,這個方法預設是返回NO,走一下步流程,如果返回YES,例如下面方法的實現,如果傳入的sel的名稱是MissMethod開頭,則認為我們的類是可以處理這個方法的。而且如果是MissMethod方法,我們就給這個類添加一個方法dynamicMethodIMP,作為MissMethod的實現。此時當外界調用MissMethod時,其實相當於調用dynamicMethodIMP

  void dynamicMethodIMP(id self, SEL _cmd) {

      NSLog(@" >> dynamicMethodIMP");

  }

 

+ (BOOL)resolveInstanceMethod:(SEL)name{    NSLog(@" >> Instance resolving %@", NSStringFromSelector(name));        NSString *selName = NSStringFromSelector(name);        if ([selName hasPrefix:@"MissMethod"]) {        if (name == @selector(MissMethod)) {            class_addMethod([self class], name, (IMP)dynamicMethodIMP, "[email protected]:");            return YES;        } else {            return NO;        }    }        return [super resolveInstanceMethod:name];}

如果resolveInstanceMethod方法返回NO了,下面怎麼辦?首先我定義了一個類MyString2作為接盤俠,實現了MissMethod1,MissMethod2方法

@interface MyString2 : NSString@end@implementation MyString2- (void)MissMethod2{    NSLog(@"MissMethod2");}- (void)MissMethod3{    NSLog(@"MissMethod3");}@end

當resolveInstanceMethod方法返回NO了,系統會嘗試調用下面方法,看看有沒有接盤俠來接這個鍋,_str2是MyString2的一個樣本,而且實現了MissMethod2方法。當MyString的樣本調用MissMethod2方法,MissMethod2->resolveInstanceMethod(NO)->forwardingTargetForSelector,返回一個接盤俠去給這個樣本發送訊息objc_sendmsg(_str2,sel)

- (id)forwardingTargetForSelector:(SEL)sel {    if(sel == @selector(MissMethod2)){        return _str2;    }    return [super forwardingTargetForSelector:sel];}

當forwardingTargetForSelector也無法處理,返回nil時,下面會走到這個方法methodSignatureForSelector,判斷sig是否為nil,如果不為nil會走forwardInvocation,最後調用forwardInvocation,如果這個方法也沒有處理,做了最後嘗試之後也就會拋出那個異常doesNotRecognizeSelector

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {        NSMethodSignature *sig;    sig = [_str2 methodSignatureForSelector:sel];    if (sig) {        return sig;    }    return [super methodSignatureForSelector:sel];}- (void)forwardInvocation:(NSInvocation *)invocation {    id target = nil;    if ([_str2 methodSignatureForSelector:[invocation selector]] ) {        target = _str2;        [invocation invokeWithTarget:target];    }}

總結一下訊息轉寄的整個流程,大致流程MissMethod->resolveInstanceMethod(NO)->forwardingTargetForSelector(nil)->methodSignatureForSelector(sig)->forwardInvocation。

下面是完整代碼實現:

////  MyString.m//  objc////  Created by zilong.li on 2017/8/17.////#import "MyString.h"#import <objc/runtime.h>@interface MyString2 : NSString@end@implementation MyString2- (void)MissMethod2{    NSLog(@"MissMethod2");}- (void)MissMethod3{    NSLog(@"MissMethod3");}@endvoid dynamicMethodIMP(id self, SEL _cmd) {    NSLog(@" >> dynamicMethodIMP");}@interface MyString (){    MyString2 *_str2;}@end@implementation MyString- (instancetype)init{    self = [super init];    if (self) {        _str2 = [[MyString2 alloc] init];    }    return self;}+ (BOOL)resolveInstanceMethod:(SEL)name{    NSLog(@" >> Instance resolving %@", NSStringFromSelector(name));        NSString *selName = NSStringFromSelector(name);        if ([selName hasPrefix:@"MissMethod"]) {        if (name == @selector(MissMethod)) {            class_addMethod([self class], name, (IMP)dynamicMethodIMP, "[email protected]:");            return YES;        } else {            return NO;        }    }        return [super resolveInstanceMethod:name];}- (id)forwardingTargetForSelector:(SEL)sel {    if(sel == @selector(MissMethod2)){        return _str2;    }    return [super forwardingTargetForSelector:sel];}- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {        NSMethodSignature *sig;    sig = [_str2 methodSignatureForSelector:sel];    if (sig) {        return sig;    }    return [super methodSignatureForSelector:sel];}- (void)forwardInvocation:(NSInvocation *)invocation {    id target = nil;    if ([_str2 methodSignatureForSelector:[invocation selector]] ) {        target = _str2;        [invocation invokeWithTarget:target];    }}@end

 

iOS訊息轉寄學習筆記

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.