Runtime-消息轉發詳解

 

消息轉發分爲三步,詳細分解如下:

第一步

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

第二步 

- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

第三步 

- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");

首先來看看第一步,都是類方法,也就是在類方法層面來解決轉發,有2個方法,第一個爲解決類方法的轉發,第二個爲解決實例方法的轉發,我們會分別舉例說明,首先定義個類

///.h

@interface MessageForwarding : NSObject

@end

///.m

#import "MessageForwarding.h"
#import <objc/runtime.h>
void TestMsgForwardingIMP(id obj,SEL sel){
    NSLog(@"success1");
}
@implementation MessageForwarding
+(BOOL)resolveClassMethod:(SEL)sel{
    const char *selName = sel_getName(sel);
    if (strcmp(selName, "TestMsgForwarding") == 0) {
        class_addMethod([self superclass], sel, [self methodForSelector:@selector(TestMsgForwardingIMP1)], "v@:");
        return YES;
    }
    return [super resolveClassMethod:sel];
}
+(void)TestMsgForwardingIMP1{
    NSLog(@"success");
}
@end

 在上面類的方法裏面,我們可以看到class_addMethod方法,那麼我們先看下官方穩定裏面怎麼定義的

/** 
 * Adds a new method to a class with a given name and implementation.
 * 
 * @param cls The class to which to add a method.
 * @param name A selector that specifies the name of the method being added.
 * @param imp A function which is the implementation of the new method. The function must take at least two arguments—self and _cmd.
 * @param types An array of characters that describe the types of the arguments to the method. 
 * 
 * @return YES if the method was added successfully, otherwise NO 
 *  (for example, the class already contains a method implementation with that name).
 *
 * @note class_addMethod will add an override of a superclass's implementation, 
 *  but will not replace an existing implementation in this class. 
 *  To change an existing implementation, use method_setImplementation.
 */
OBJC_EXPORT BOOL
class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, 
                const char * _Nullable types) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

現在咱們看下執行代碼和結果

[MessageForwarding performSelector:@selector(TestMsgForwarding)];

打印結果如下:

OCTest[11268:1533041] success

切換成TestMsgForwardingIMP(C的方式)這個定義

#import "MessageForwarding.h"
#import <objc/runtime.h>
void TestMsgForwardingIMP(id self,SEL sel){
    NSLog(@"success1");
}
@implementation MessageForwarding
+(BOOL)resolveClassMethod:(SEL)sel{
    const char *selName = sel_getName(sel);
    if (strcmp(selName, "TestMsgForwarding") == 0) {
//        class_addMethod([self superclass], sel, [self methodForSelector:@selector(TestMsgForwardingIMP1)], "v@:");
        class_addMethod([self superclass], sel, (IMP)TestMsgForwardingIMP, "v@:");
        return YES;
    }
    return [super resolveClassMethod:sel];
}
+(void)TestMsgForwardingIMP1{
    NSLog(@"success");
}
@end

繼續執行結果如下:

OCTest[11416:1558454] success1

實例方法的轉發,代碼如下:

+(BOOL)resolveInstanceMethod:(SEL)sel{
    const char *selName = sel_getName(sel);
    if (strcmp(selName, "testInstanceMsgForwarding") == 0) {
        class_addMethod(self, sel,[self instanceMethodForSelector:@selector(testInstanceMsgForwardingIMP)], "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
-(void)testInstanceMsgForwardingIMP{
    NSLog(@"instance success");
}

調用代碼如下

MessageForwarding *mf = [MessageForwarding new];
[mf performSelector:@selector(testInstanceMsgForwarding)];

執行結果如下:

OCTest[11656:1582545] instance success

第二步

修改函數的執行對象,代碼如下:

#import "MessageForwarding.h"
@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    [self performSelector:@selector(testInstanceMsgForwarding)];
}
-(id)forwardingTargetForSelector:(SEL)aSelector{
    if (strcmp(sel_getName(aSelector), "testInstanceMsgForwarding") == 0) {
        return [MessageForwarding new];
    }
    return [super forwardingTargetForSelector:aSelector];
}
@end

執行結果如下:

OCTest[11702:1587797] instance success

第三步

隨意指定對象執行相應函數,代碼如下:

#import "MessageForwarding.h"
@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    [self performSelector:@selector(testInstanceMsgForwarding)];
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if (strcmp(sel_getName(aSelector), "testInstanceMsgForwarding") == 0){
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
    MessageForwarding *mf = [MessageForwarding new];
    if ([mf respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:mf];
    }else{
        [self doesNotRecognizeSelector:anInvocation.selector];
    }
}
@end

執行結果如下:

OCTest[11778:1594036] instance success

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章