方法調(diào)用的方式
在iOS開發(fā)中惑朦,直接調(diào)用方法的方式有以下兩種:
- (id)performSelector:(SEL)aSelector
- 使用NSInvocation對(duì)象
performSelector比較常用,但是傳遞的參數(shù)有限漓概,以下是定義
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
This method is the same as performSelector: except that you can supply two arguments for
aSelector
.aSelector
should identify a method that can take two arguments of type id. For methods with other argument types and return values, use NSInvocation.
從官方的說明可以看到漾月,這種方式比較局限,例如只能最多傳遞兩個(gè)參數(shù)胃珍,超過兩個(gè)參數(shù)的話梁肿,官方建議使用NSInvocation對(duì)象來實(shí)現(xiàn)方法的直接調(diào)用。使用NSInvocation可以更加方便地更改方法調(diào)用的參數(shù)觅彰,包含直接調(diào)用iOS的私有方法吩蔑,更改參數(shù)值等。
NSInvocation
NSInvocation是一個(gè)消息調(diào)用類填抬,主要作用是存儲(chǔ)和傳遞消息烛芬。它存儲(chǔ)的信息包含了一個(gè)iOS消息全部的成分:target、selector飒责、參數(shù)赘娄、返回值、方法簽名
读拆。
也就是說,NSInvocation可以將傳統(tǒng)的iOS消息發(fā)送
這個(gè)過程轉(zhuǎn)換成一個(gè)對(duì)象鸵闪,然后執(zhí)行這個(gè)對(duì)象的發(fā)送消息
的方法就可以達(dá)到performSelector的效果檐晕,NSInvocation對(duì)象包含的每一個(gè)組成部分能夠直接設(shè)定(如消息target,參數(shù)之類的)。
NSInvocation創(chuàng)建
創(chuàng)建NSInvocation只能使用+ (NSInvocation *)invocationWithMethodSignature:(NSMethodSignature *)sig
方法辟灰,不能使用alloc或者init方式來創(chuàng)建个榕。創(chuàng)建的參數(shù)是方法簽名,這個(gè)從上一點(diǎn)就知道芥喇,NSInvocation也包含了方法的全部組成(包括方法簽名)西采,下面就說說方法簽名NSMethodSignature是什么?
NSMethodSignature
方法簽名 NSMethodSignature 是一個(gè)方法的返回類型
和參數(shù)類型
继控,不包括方法名稱
械馆。
創(chuàng)建NSMethodSignture主要是以下兩個(gè)方法
+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector ;
+ (nullable NSMethodSignature *)signatureWithObjCTypes:(const char *)types;
例如如下創(chuàng)建一個(gè)方法簽名
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"@@:*"];
其中字符串@@:*
-
@
表示返回值為id -
@:
表示方法target和sel,iOS的每個(gè)方法都有這兩個(gè)隱藏參數(shù) -
*
表示一個(gè)char*類型的參數(shù)
關(guān)于第二點(diǎn)@:
可以參照objc_msgSend的聲明武通,括號(hào)里面的第一個(gè)就是id類型的target霹崎,第二個(gè)就是SEL類型的選擇子,這是每個(gè)iOS方法的兩個(gè)隱藏參數(shù)冶忱,如下所示
id _Nullable objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
使用@encode()可以得到某一種類型的符號(hào)編碼尾菇,如下
typeEncodeMethod這個(gè)方法的符號(hào)為i@:f@
-(int)typeEncodeMethod:(float)floatValue arr:(NSArray *)arr
{
Method method = class_getInstanceMethod(self.class, @selector(typeEncodeMethod:arr:));
const char *des = method_getTypeEncoding(method);
NSString *desStr = [NSString stringWithCString:des encoding:NSUTF8StringEncoding];
NSLog(@"%@",desStr);
char *buf = @encode(NSObject *);
NSLog(@"encode: %s ", buf);
buf = @encode(float);
NSLog(@"encode: %s ", buf);
buf = @encode(NSString *);
NSLog(@"encode: %s ", buf);
NSLog(@"typeEncodeMethod");
return 0;
}
//log 輸出如下
2018-11-14 16:23:05.375422+0800 CrashAvoid[6399:174637] i28@0:8f16@20
2018-11-14 16:23:05.375491+0800 CrashAvoid[6399:174637] encode: @
2018-11-14 16:23:05.375549+0800 CrashAvoid[6399:174637] encode: f
2018-11-14 16:23:05.375616+0800 CrashAvoid[6399:174637] encode: @
2018-11-14 16:23:05.375689+0800 CrashAvoid[6399:174637] typeEncodeMethod
這些符號(hào)表可以從官方里面查看
Type Encodings
使用小例子
下面是一個(gè)使用小例子,調(diào)用iOS私有方法強(qiáng)制橫屏
- (void)interfaceOrientation:(UIInterfaceOrientation)orientation
{
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
SEL selector = NSSelectorFromString(@"setOrientation:");
NSInvocation * invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
[invocation setSelector:selector];
[invocation setTarget:[UIDevice currentDevice]];
int val = orientation;
[invocation setArgument:&val atIndex:2];
[invocation invoke];
}
}
注意以上代碼[invocation setArgument:&val atIndex:2];
囚枪,index 為 2派诬,NSInvocation的前面兩個(gè)隱藏參數(shù)為(id _Nullable self, SEL _Nonnull op, ...),方法的參數(shù)從index = 2開始链沼。