消息轉發分爲三步,詳細分解如下:
第一步
+ (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