NSInvocation的基本用法

小知識:

在 iOS中可以直接調用某個對象的消息方式有兩種:
一種是performSelector:withObject;
再一種就是NSInvocation。

為什么這么說呢,首先某個對象指的實例化后的對象,對象所對應的方法,則指的是實例方法。在一個類中調用另一個類的實例方法,只有上面兩種方式。第一種方式比較簡單,能完成簡單的調用。但是對于>2個的參數或者有返回值的處理,那performSelector:withObject就顯得有點有心無力了,那么在這種情況下,我們就可以使用NSInvocation來進行這些相對復雜的操作

NSInvocation的基本使用

一、直接調用當前類對象消息

方法簽名類

  // 方法簽名中保存了方法的名稱/參數/返回值,協同  NSInvocation來進行消息的轉發
 // 方法簽名一般是用來設置參數和獲取返回值的, 和方法的調用沒有太大的關系
//1、根據方法來初始化NSMethodSignature
NSMethodSignature  *signature = [ViewController    instanceMethodSignatureForSelector:@selector(run:)];

根據方法簽名來創建NSInvocation對象

 // NSInvocation中保存了方法所屬的對象/方法名稱/參數/返回值
//其實NSInvocation就是將一個方法變成一個對象
//2、創建NSInvocation對象
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
//設置方法調用者
invocation.target = self;
//注意:這里的方法名一定要與方法簽名類中的方法一致
invocation.selector = @selector(run:);
NSString *way = @"byCar";
//這里的Index要從2開始,以為0跟1已經被占據了,分   別是self(target),selector(_cmd)
[invocation setArgument:&way atIndex:2];
//3、調用invoke方法
[invocation invoke];
//實現run:方法
- (void)run:(NSString *)method{

 }

二、在當前VC調用其他類對象消息

這里咱們假設一種情況需要在VC控制器中調用繼承自NSObject的ClassBVc類中的run方法,并且要向該方法傳遞參數。

ClassBVc.h文件實現

#import <UIKit/UIKit.h>

@interface ClassBVc : UIViewController



 - (void)run:(NSString *)parames;

@end

ClassBVc.m文件實現

#import "ClassBVc.h"

@interface ClassBVc ()

@end

@implementation ClassBVc

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}

 - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (void)run:(NSString *)parames{

 NSLog(@"你猜我猜不猜?%@",parames);
}

VC.m文件實現

#import "ViewController.h"
#import "ClassBVc.h"


@interface ViewController ()

@end

@implementation ViewController



- (void)viewDidLoad {
[super viewDidLoad];

ClassBVc *B = [[ClassBVc alloc]init];
// 方法簽名中保存了方法的名稱/參數/返回值,協同  NSInvocation來進行消息的轉發
// 方法簽名一般是用來設置參數和獲取返回值的, 和方法的調用沒有太大的關系
//1、根據方法來初始化NSMethodSignature
NSMethodSignature  *signature = [ClassBVc    instanceMethodSignatureForSelector:@selector(run:)];

// NSInvocation中保存了方法所屬的對象/方法名稱/參數/返回值
//其實NSInvocation就是將一個方法變成一個對象
//2、創建NSInvocation對象
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
//設置方法調用者
invocation.target = B;
//注意:這里的方法名一定要與方法簽名類中的方法一致
invocation.selector = @selector(run:);
NSString *way = @"byCar";
//這里的Index要從2開始,以為0跟1已經被占據了,分   別是self(target),selector(_cmd)
[invocation setArgument:&way atIndex:2];
//3、調用invoke方法
[invocation invoke];
}

最終運行結果:


NSInvocation在項目中最重要的使用場景在消息轉發上面,如果在做消息轉發時你沒有實現resolveClassMethod:或者forwardingTargetForSelector:方法,那么系統就會來到最后攔截的第三道屏障,當你實現methodSignatureForSelector:方法以后,系統會觸發forwardInvocation:方法;在forwardInvocation:方法中你可以修改目標的接受者等等。

下面咱們來復現該場景:

假設現在有一個VC控制器、兩個繼承自NSObject的Target類和boy類,現在咱們需要在VC里面調用Target的methodC方法,但是在Target類里面我只寫了methodC方法的聲明,并沒有在.m中實現它,但是我在Boy.m類中實現了methodC方法,現在需要做到的就是在調用Target方法時,觸發Boy類中methodC的實現。

VC.m文件中的實現:

#import "ViewController.h"
#import "Target.h"


@interface ViewController ()

@end

@implementation ViewController



- (void)viewDidLoad {
[super viewDidLoad];
Target *targets = [[Target alloc]init];
[targets methodC];


}

Target.h文件中中的實現

#import <Foundation/Foundation.h>

@interface Target : NSObject


- (void)methodC;

@end

Target.m文件中把消息轉發給了Boy類來執行

#import "Target.h"
#import "Boy.h"
#import <objc/runtime.h>


@implementation Target


//methodSignatureForSelector用來生成方法簽名,這個簽    名就是給forwardInvocation中的參數NSInvocation調用的。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{

NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
if (!signature) {
    if ([Boy instancesRespondToSelector:aSelector]) {
        signature = [Boy instanceMethodSignatureForSelector:aSelector];
    }
}
return signature;
}

//所以我們需要做的是自己新建方法簽名,再在     forwardInvocation中用你要轉發的那個對象調用這個對應的簽    名,這樣也實現了消息轉發。
- (void)forwardInvocation:(NSInvocation *)anInvocation{

if ([Boy  instancesRespondToSelector:anInvocation.selector]) {
    [anInvocation invokeWithTarget:[Boy new]];
}
}

Boy.h文件中沒有實現

#import <Foundation/Foundation.h>

@interface Boy : NSObject

@end

Boy.m文件中實現了methodC方法

#import "Boy.h"

@implementation Boy

- (void)methodC{

NSLog(@"愛我你就抱抱我");
}

@end

從上面可以看到,當咱們去調用類中沒有找到實現方法的時候,如果我們在消息轉發的過程中重寫了那些方法,就可以實現一些特定的需求。

具體的demo地址在我個人github上面,如果有需要的朋友歡迎下載->傳送門

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 前提: 在 iOS中可以直接調用某個對象的消息方式有兩種:一種是performSelector:withObjec...
    有一種再見叫青春閱讀 578評論 0 2
  • 前提: 在 iOS中可以直接調用某個對象的消息方式有兩種:一種是performSelector:withObjec...
    木_木27閱讀 19,310評論 11 71
  • 轉至元數據結尾創建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數據起始第一章:isa和Class一....
    40c0490e5268閱讀 1,789評論 0 9
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,993評論 19 139
  • 1.import static是Java 5增加的功能,就是將Import類中的靜態方法,可以作為本類的靜態方法來...
    XLsn0w閱讀 1,267評論 0 2