傳遞不定長的多個參數
/*iOS實現傳遞不定長的多個參數的方法是使用va_list。va_list是C語言提供的處理變長參數的一種方法。在調用的時候要在參 數結尾的時候加nil
va_list的使用需要注意:
1.首先在函數里定義va_list型的變量,這個變量是指向參數的指針;
2.然后用va_start初始化剛定義的va_list變量;
3.然后用va_arg返回可變的參數,va_arg的第二個參數是你要返回的參數的類型.如果函數有多個可變參數的,依次調用va_arg獲取各個參數;
4.最后用va_end宏結束可變參數的獲取
NS_REQUIRES_NIL_TERMINATION,是一個宏,用于編譯時非nil結尾的檢查。 調用時要以nil結尾,否則會崩潰。
*/
- (void)testParams:(NSString *)title addMoreParams:(NSString *)string, ...NS_REQUIRES_NIL_TERMINATION {
NSLog(@"傳多個參數的第一個參數 %@",string);//是other1
//1.定義一個指向個數可變的參數列表指針;
va_list args;
//2.va_start(args, str);string為第一個參數,也就是最右邊的已知參數,這里就是獲取第一個可選參數的地址.使參數列表指針指向函數參數列表中的第一個可選參數,函數參數列表中參數在內存中的順序與函數聲明時的順序是一致的。
va_start(args, string);
if (string)
{
//依次取得除第一個參數以外的參數
//4.va_arg(args,NSString):返回參數列表中指針所指的參數,返回類型為NSString,并使參數指針指向參數列表中下一個參數。
while (va_arg(args, NSString *))
{
NSString *otherString = va_arg(args, NSString *);
NSLog(@"otherString %@",otherString);
}
}
//5.清空參數列表,并置參數指針args無效。
va_end(args);
}
performSelector多參數傳遞解決方案
解決方案:
- 使用NSInvocation進行消息轉發從而實現對performSelector的多參數傳遞。
- 使用runtime中的objc_msgSend進行消息的發送。
方案一:
使用NSInvocation進行消息轉發從而實現對performSelector的多參數傳遞。
- (void)viewDidLoad {
[super viewDidLoad];
id object = [self glt_performSelector:@selector(testMoreArg:arg2:arg3:) withObject:@"username",@"sex",@"height", nil];
NSLog(@"%@",object);
}
- (NSString *)testMoreArg:(NSString *)arg1 arg2:(NSString *)arg2 arg3:(NSString *)arg3{
NSLog(@"arg1 = %@ arg2 = %@ arg3 = %@",arg1,arg2,arg3);
return [NSString stringWithFormat:@"%@%@%@",arg1,arg2,arg3];
}
-(id)glt_performSelector:(SEL)selector withObject:(id)object,...NS_REQUIRES_NIL_TERMINATION{
//根據類名以及SEL 獲取方法簽名的實例
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:selector];
if (signature == nil) {
NSLog(@"--- 使用實例方法調用 為nil ---");
signature = [self methodSignatureForSelector:selector];
if (signature == nil) {
NSLog(@"使用類方法調用 也為nil, 此時return");
return nil;
}
}
//NSInvocation是一個消息調用類,它包含了所有OC消息的成分:target、selector、參數以及返回值。
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = self;
invocation.selector = selector;
NSUInteger argCount = signature.numberOfArguments;
// 參數必須從第2個索引開始,因為前兩個已經被target和selector使用
argCount = argCount > 2 ? argCount - 2 : 0;
NSMutableArray *objs = [NSMutableArray arrayWithCapacity:0];
if (object) {
[objs addObject:object];
va_list args;
va_start(args, object);
while ((object = va_arg(args, id))){
[objs addObject:object];
}
va_end(args);
}
if (objs.count != argCount){
NSLog(@"--- objs.count != argCount! please check it! ---");
return nil;
}
//設置參數列表
for (NSInteger i = 0; i < objs.count; i++) {
id obj = objs[i];
if ([obj isKindOfClass:[NSNull class]]) {
continue;
}
[invocation setArgument:&obj atIndex:i+2];
}
[invocation invoke];
//獲取返回值
if (signature.methodReturnLength != 0 && signature.methodReturnLength) {
void *returnValue;
[invocation getReturnValue:&returnValue];
return (__bridge id)returnValue;
}
return nil;
}
方案二:
參考之前的文章:
Runtime objc_msgSend
參考資料
iOS實現傳遞不定長的多個參數
iOS performSelector多參數傳遞解決方案以及objc_msgSend的使用注意事項
NSInvocation在獲取返回值后crash問題