2012-06-20 98 views
3

對於客觀C來說,我很新奇,並想知道是否有人能夠幫助我解決這個問題。我有一些不同的方法,每一個都需要3個輸入值和正常使用目標C使用字符串來調用方法

[self methodA:1 height:10 speed:3] 

調用它,但該方法的名字,我想從一個字符串中的plist看了這麼例如,如果字符串是的methodB我會得到

[self methodB:1 height:10 speed:3] 

爲 「methodC」

[self methodC:1 height:10 speed:3] 

等。

任何想法我怎麼可能做到這一點我試圖定義字符串使用NSSelectorFromString

NSString *string = [plistA objectForKey:@"method"]; 
SEL select = NSSelectorFromString(string); 
[self performSelector:select:c height:b speed:a]; 

然而,這並沒有工作,要麼任何幫助,將不勝感激一個選擇。 已經嘗試了下面的解決方案,但無法在這裏工作是我試過的。

所以剛開始我有方法,如

spawnEnemyA:2 withHeight:3 withSpeed:4 
spawnEnemyB:3 withHeight:2 withSpeed:5 

,我想讀書,我想通過這些方法,以及從plist文件的方法類型的值。我的代碼如下,//////////////////////////////////////////// //////////////////

//這是我從我想要的plist中讀取的值我的方法使用

int a = [[enemySettings objectForKey:@"speed"] intValue]; 
    int b = [[enemySettings objectForKey:@"position"] intValue]; 
    int c = [[enemySettings objectForKey:@"delay"] intValue]; 

    // I Also read the method name from the plist and combine it into a single string 
    NSString *method = [enemySettings objectForKey:@"enemytype"]; 
    NSString *label1 = @"spawn"; 
    NSString *label2 = @":withHeight:withSpeed:"; 
    NSString *combined = [NSString stringWithFormat:@"%@%@%@",label1, method,label2]; 


    //Check that the string is correct get spawnEnemyA:withHeight:withSpeed: 
    CCLOG(@"%@",combined); 


//This is the Invocation part 
    NSInvocation * invocation = [ NSInvocation new ]; 

    [ invocation setSelector: NSSelectorFromString(combined)]; 
    [ invocation setArgument: &c atIndex: 2 ]; 
    [ invocation setArgument: &b atIndex: 3 ]; 
    [ invocation setArgument: &a atIndex: 4 ]; 

    [ invocation invokeWithTarget:self ]; 

    [invocation release ]; 

/// ////////////////////////////////////////////////// ///////////////

代碼編譯時沒有任何錯誤,但方法未被調用。有任何想法嗎?乾杯

回答

1

您可以直接使用objc_msgsend

NSString *methodName = [plistA objectForKey:@"method"]; 
objc_msgSend(self, methodName, c, b, a); 

記住,選擇必須包括所有作品,如@"method:height:speed:"

+0

您需要將調用轉換爲objc_msgSend()才能使其正確。 – bbum

+1

初學者可能有點低級; )還要記住,如果你需要一個返回值,還有'objc_msgSend_fpret'(浮點返回)和'objc_msgSend_stret'(結構返回)。 – Macmade

+0

是的,我可以想象它可能有點低級,但至少你可以學習Obj-C是如何構建在C上的:)在這裏查看關於這個方法的更多細節:http://stackoverflow.com/questions/2573805/using-objc-msgsend-to-call-a-objective-c-function-with-named-arguments – Jack

11

不能爲一個方法使用performSelector 3(或更多)的參數。
但對於您的信息,這裏是如何使用它:

SEL m1; 
SEL m2; 
SEL m3; 

m1 = NSSelectorFromString(@"someMethodWithoutArg"); 
m2 = NSSelectorFromString(@"someMethodWithAnArg:"); 
m1 = NSSelectorFromString(@"someMethodWithAnArg:andAnotherOne:"); 

[ someObject performSelector: m1 ]; 
[ someObject performSelector: m2 withObject: anArg ]; 
[ someObject performSelector: m2 withObject: anArg withObject: anOtherArg ]; 

對於超過2個參數,你將不得不使用NSInvocation類的方法。
查看文檔以瞭解如何使用它。

基本上是:

NSInvocation * invocation = [ NSInvocation new ]; 

[ invocation setSelector: NSStringFromSelector(@"methodWithArg1:arg2:arg3:") ]; 

// Argument 1 is at index 2, as there is self and _cmd before 
[ invocation setArgument: &arg1 atIndex: 2 ]; 
[ invocation setArgument: &arg2 atIndex: 3 ]; 
[ invocation setArgument: &arg3 atIndex: 4 ]; 

[ invocation invokeWithTarget: targetObject ]; 

// If you need to get the return value 
[ invocation getReturnValue: &someVar ]; 

[ invocation release ]; 
+3

我看到空間的粉絲。 :)這也會起作用。對於像這樣的調用,我更喜歡''objc_msgSend()'速度和最重要的是,鍵入安全性,因爲編譯器會抱怨,如果我以後搞砸了類型。 – bbum

+0

我完全同意速度。但對於初學者來說,我認爲Objective-C的方式可能不太令人困惑,即使知道運行時如何運作非常重要。我不同意類型安全,因爲恕我直言,這兩種方式都是不安全的。這就是'objc_msgSend'的用途。否則不會是通用的。無論如何,感謝您的評論。 :)(和btw,是的,我愛空間!) – Macmade

+0

實際上,假設你獲得了正確的演員陣容,演員'objc_msgSend()'是更安全的類型。調用的參數實際上是無類型的。而且該代碼是錯誤的;你需要傳遞參數的地址(我沒有聽到第一次讀到的)!所有那些指針魔術goop肯定會增加很多複雜性。 – bbum

-1

您應該替換以下行:

[self performSelector:select:c height:b speed:a]; 

和寫入以下內容:

[self performSelector:select withObject:[NSArray arrayWithObjects:c,b,a,nil]]; 
+0

這將無法正常工作。 – bbum

4

在一般情況下,這種活力往往預示反-模式。以這種方式將數據與實施結合起來通常不是最佳實踐。

雖然有時需要。如果你走這條路,然後給你的各種方法的聲明很可能是這樣的:

- (void)methodAWidth:(NSUInteger)w height:(NSUInteger)h speed:(NSUInteger)s; 
- (void)methodBWidth:(NSUInteger)w height:(NSUInteger)h speed:(NSUInteger)s; 
- (void)methodCWidth:(NSUInteger)w height:(NSUInteger)h speed:(NSUInteger)s; 

你可能會想是這樣的:

NSString *selName = [NSString stringWithFormat:@"method%@Width:height:speed:", ... one of @"A", @"B", or @"C" ....]; 
SEL selector = NSelectorFromString(selName); 

然後:

if (![target respondsToSelector:selector]) 
    return; // no can do 

void (*castMsgSend)(id, SEL, NSUInteger, NSUInteger, NSUInteger) = (void*)objc_msgSend; 
castMsgSend(target, selector, 1, 10, 3); 

每方法調用被編譯爲調用objc_msgSend()。通過上述操作,您正在創建一個完全類型安全的/類型檢查的呼叫站點,它經歷了正常的Objective-C消息傳遞機制,但是選擇器是動態定義的。\

雖然performSelector:(和多-arg變體)非常方便,它們不能處理非對象類型。


而且,正如MacMade在評論中指出的,注意浮點和結構返回。它們使用objc_msgSend()的不同變體,編譯器在正常的[foo bar]大小寫中自動處理。

+0

當我嘗試這個時,我得到了void(castMSgSend)部分的以下錯誤消息無法初始化類型爲'void *'的右值類型'void()(id,SEL,NSUInteger,NSUInteger,NSUInteger)'的變量如何我能解決這個問題嗎?歡呼@bbum – tomo