2013-02-01 41 views
2

我想動態地爲當前方法創建NSInvocation,並使用正確的參數值。通常情況下,人們可能會做到這一點:Objective-C參數值的基於索引的提取

- (void)messageWithArg:(NSString *)arg arg2:(NSString *)arg2 
{ 
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:_cmd]]; 
    [invocation setTarget:self]; 

    /* 
    * How do we set the argument values here dynamically? 
    */ 
} 

設置參數值明確是微不足道的,我們可以做這樣的事情:

[invocation setArgument:&arg atIndex:2]; 
[invocation setArgument:&arg2 atIndex:3]; 

我希望能夠與像這樣做在一個循環這個:

for(int i = 0; i < [[self methodSignatureForSelector:_cmd] numberOfArguments]; i++) { 
    [invocation setArgument:?!?! atIndex:i + 2]; 
} 

困難的部分是動態獲取給定索引的參數值。

類似的問題被問here當回答者說,他不知道一個解決方案援引類的複雜性。我不同意複雜性 - 在底層代碼中,我們已經確切知道堆棧框架設置後應該如何看待堆棧,因爲編譯器知道使用的調用約定。例如,在x86與stdcall我們可以很容易地訪問參數值,因爲我們知道他們是固定的ebp偏移:在

  • ebp0(%EBP)
  • 4返回地址( %EBP)在8(%EBP
  • 第一個參數

我如何能實現我想要什麼或做真的不存在任何機制來支持的參數值基於索引取的語言?在這一點上,我可以接受這是真實的,因爲在C標準中不存在這樣的特徵。但是,我希望得到確認和/或解釋背後的推理。

+0

都是這樣阿根廷,ARG2,ARG3,ARG4等參數的東西...? –

+0

簽名不會是可變的,您可以假設參數類型由不可變對象或基元組成。 –

+0

好的,但參數名稱,預計會是什麼?如果它們都像arg3那樣編號,arg4很容易做到。如果你可以把它們放在一個數組中,更容易。讓我知道。 –

回答

0

這個工作,但不是我所期待。 va_start used in function with fixed arguments錯誤使我無法在常規方法中使用va_start。取決於你想要達到的目標可能是有用的。

@interface Test : NSObject 

- (void)method:(id)arg1 :(id)arg2; 

@end 

@implementation Test 

+ (void)load { 
    class_addMethod([Test class], @selector(method::), (IMP)method_imp, "[email protected]:@@"); 
} 

void method_imp(id self, SEL _cmd, ...) { 
    va_list ap; 
    va_start(ap, _cmd); 
    SEL sel = NSSelectorFromString([@"_" stringByAppendingString:NSStringFromSelector(_cmd)]); 
    NSMethodSignature *signature = [self methodSignatureForSelector:sel]; 
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 
    int argc = [signature numberOfArguments]; 
    char *ptr = (char *)ap; 
    for (int i = 2; i < argc; i++) { 
     const char *type = [signature getArgumentTypeAtIndex:i]; 
     [invocation setArgument:ptr atIndex:i]; 
     NSUInteger size; 
     NSGetSizeAndAlignment(type, &size, NULL); 
     ptr += size; 
    } 
    va_end(ap); 
    [invocation setSelector:sel]; 
    [invocation invokeWithTarget:self]; 
} 

- (void)_method:(id)arg1 :(id)arg2 { 
    NSLog(@"%@, %@, %@", NSStringFromSelector(_cmd), arg1, arg2); 
} 

@end 

呼叫method::將結束_method::並沒有什麼硬編碼

Test *test = [[Test alloc] init]; 
[test method:@"arg1" :@"arg2"]; // log: _method::, arg1, arg2 
+0

感謝您的答案 - 這將肯定適用於各種方法,但我已經指定了我的不是也不能更改。 –

+0

好吧,你仍然可以嘗試。我使用這段代碼來創建'IMP'函數,並在運行時將它添加到類中,這對我來說非常有用。 –

+0

我不認爲這是便攜式。 (id,SEL,id,id)函數的調用約定可能與(id,SEL,...)函數的調用約定不同。編譯器構造方法調用,假定接口中的方法簽名。如果接收它的實際函數具有不同的調用約定,它會搞亂。 – newacct