2012-12-04 10 views
5

मेरे पास कुछ तर्क स्वीकार करने वाले ब्लॉक के साथ एक चर है। तर्कों और उनके प्रकार की सटीक संख्या भिन्न हो सकती है। उदाहरण के लिए यह एक ब्लॉकब्लॉक के तर्क संख्या और प्रकार भिन्न हो सकते हैं जब va_list से तर्क के साथ कॉल ब्लॉक

void(^testBlock1)(int) = ^(int i){} 

या एक ब्लॉक

void(^testBlock2)(NSString *,BOOL,int,float) = ^(NSString *str,BOOL b,int i,float f){} 

तर्क प्रकार {id, BOOL, char, int, unsigned int, float} तक ही सीमित हैं हो सकता है।

मुझे तर्कों और उनके प्रकार की वर्तमान गणना पता है। मैं एक विधि है जो दिए गए तर्क के साथ ब्लॉक निष्पादित कर सकते हैं लागू करने की आवश्यकता:

-(void)runBlock:(id)block withArguments:(va_list)arguments 
      types:(const char *)types count:(NSUInteger)count; 

मेरे पास अनुभवहीन समाधान काम कर रहा है, लेकिन यह काफी बदसूरत है, अधिक से अधिक 4 बाइट आकार का केवल प्रकार का समर्थन करता है और संरेखण पर निर्भर करता है। तो मैं कुछ बेहतर खोज रहा हूँ।

#define MAX_ARGS_COUNT 5 
-(void)runBlock:(id)block withArguments:(va_list)arguments 
      types:(const char *)types count:(NSUInteger)count{ 

    // We will store arguments in this array. 
    void * args_table[MAX_ARGS_COUNT]; 

    // Filling array with arguments 
    for (int i=0; i<count; ++i) { 
     switch (types[i]) { 
      case '@': 
      case 'c': 
      case 'i': 
      case 'I': 
       args_table[i] = (void *)(va_arg(arguments, int)); 
       break; 
      case 'f': 
       *((float *)(args_table+i)) = (float)(va_arg(arguments, double)); 
       break; 
      default: 
       @throw [NSException exceptionWithName:@"runBlock" reason:[NSString stringWithFormat:@"unsupported type %c",types[i]] userInfo:nil]; 
       break; 
     } 
    } 

    // Now we need to call our block with appropriate count of arguments 

#define ARG(N) args_table[N] 

#define BLOCK_ARG1 void(^)(void *) 
#define BLOCK_ARG2 void(^)(void *,void *) 
#define BLOCK_ARG3 void(^)(void *,void *,void *) 
#define BLOCK_ARG4 void(^)(void *,void *,void *,void *) 
#define BLOCK_ARG5 void(^)(void *,void *,void *,void *,void *) 
#define BLOCK_ARG(N) BLOCK_ARG##N 

    switch (count) { 
     case 1: 
      ((BLOCK_ARG(1))block)(ARG(0)); 
      break; 
     case 2: 
      ((BLOCK_ARG(2))block)(ARG(0),ARG(1)); 
      break; 
     case 3: 
      ((BLOCK_ARG(3))block)(ARG(0),ARG(1),ARG(2)); 
      break; 
     case 4: 
      ((BLOCK_ARG(4))block)(ARG(0),ARG(1),ARG(2),ARG(3)); 
      break; 
     case 5: 
      ((BLOCK_ARG(5))block)(ARG(0),ARG(1),ARG(2),ARG(3),ARG(4)); 
      break; 
     default: 
      break; 
    } 
} 

उत्तर

6

वैसे आप के खिलाफ कमी के- मेटाडाटा और सी में ABI समस्या यहाँ क्लासिक ऊपर चल रहे हैं: मेरे समाधान की तरह कुछ है। माइक ऐश के Awesome Article about MABlockClosure के आधार पर, मुझे लगता है कि आप ब्लॉक की अंतर्निहित संरचना की जांच कर सकते हैं और मान लें कि va_list ब्लॉक की अपेक्षा करता है। आप ब्लॉक को ब्लॉक_लेआउट पर ब्लॉक कर सकते हैं, फिर ब्लॉक-> डिस्क्रिप्टर आपको स्ट्रक्चर ब्लॉक डिस्क्रिप्टर देगा। फिर आपके पास @encode स्ट्रिंग है जो ब्लॉक के तर्कों और प्रकारों का प्रतिनिधित्व करती है (@encode वर्म्स का एक और अन्य कर सकता है)।

तो एक बार जब आपके पास तर्क और उनके प्रकार की सूची हो, तो आप ब्लॉक_लेआउट में खोद सकते हैं, हमला कर सकते हैं, फिर इसे फ़ंक्शन पॉइंटर के रूप में मान सकते हैं जहां पहला पैरामीटर संदर्भ प्रदान करता है। माइक ऐश में Trampolining Blocks पर कुछ जानकारी भी है जो काम कर सकती है यदि आपको किसी भी प्रकार की जानकारी की परवाह नहीं है, लेकिन बस ब्लॉक को आमंत्रित करना चाहते हैं।

मुझे एक बड़ी वसा जोड़ें "यहां ड्रैगन हो" चेतावनी दें। यह सब बहुत स्पर्शपूर्ण है, एबीआई के आधार पर भिन्न होता है, और अस्पष्ट और/या अनियंत्रित विशेषताओं पर निर्भर करता है।

ऐसा लगता है कि आप जहां भी इसकी आवश्यकता हो, सीधे ब्लॉक को कॉल कर सकते हैं, संभवतः एनएसएआरएआरई का उपयोग केवल पैरामीटर और आईडी के रूप में वापसी प्रकार के रूप में कर सकते हैं। तो आपको किसी भी "चालाक" हैक्स बैकफायरिंग के बारे में चिंता करने की ज़रूरत नहीं है।

संपादित करें: आप एनएसएम विधि हस्ताक्षर के हस्ताक्षर WithObjCTypes का उपयोग करने में सक्षम हो सकते हैं:, ब्लॉक के हस्ताक्षर में गुजर रहा है। फिर आप एनएसआईएनवोकेशन के आमंत्रण को कॉल कर सकते हैं WithMethodSignature :, लेकिन आपको निजी invokeWithIMP को कॉल करना होगा: वास्तव में इसे ट्रिगर करने के लिए विधि क्योंकि आपके पास कोई चयनकर्ता नहीं है। आप लक्ष्य को ब्लॉक पर सेट करेंगे, फिर ब्लॉक स्ट्रक्चर के आवेक पॉइंटर को पार करते हुए, WIMIMP को आमंत्रित करें। देखें Generic Block Proxying

+1

उत्तर के लिए धन्यवाद। मेरे पास पहले से ही तर्क और उनके प्रकार की सूची है। और हाँ, मैं _invoke_ फ़ंक्शन पॉइंटर ढूंढ और पकड़ सकता हूं। लेकिन मैं इसे विशेष रूप से अपने 'BLOCK_ARG (N) 'मैक्रोज़ के लिए कास्टिंग और तर्कों के लिए एक अस्थायी सरणी से बेहतर तरीके से कैसे कॉल कर सकता हूं? क्या 'ffi_call' को छोड़कर कोई अन्य समाधान है? ** libffi का उपयोग ** मेरे छोटे काम के लिए बहुत अधिक दिखता है। – Yan

+0

वैसे यह बात है - आपको सी उम्मीदों के तरीके में पैरामीटर सेट अप करने के लिए असेंबली का उपयोग करना होगा। यही एनएसआईएनवोकेशन और ऑब्जेक्टिव-सी रनटाइम के कई अन्य गड़बड़ है क्योंकि सी में कुछ रजिस्टरों में तर्कों को निर्धारित किया जाना चाहिए, जो एक निश्चित तरीके से ढेर पर फैलते हैं। प्लेटफ़ॉर्म (x86, x64, ARM) के आधार पर यह परिवर्तन। – russbishop

0

निजी आमंत्रण का उपयोग करने के लिए एक अच्छा विकल्प WithIMP: जिस विधि को आप अलग-अलग कार्यान्वयन करना चाहते हैं, उसे स्विज करना है, और इसे किसी भी समय इच्छित आईएमपी की तलाश करना है। हम कुछ इसी प्रकार का उपयोग करते हैं: https://github.com/tuenti/TMInstanceMethodSwizzler

0

वास्तविक समाधान एक va_list पैरामीटर के साथ एक ब्लॉक होना है और ब्लॉक को स्वयं को हल करने देना है। यह एक सिद्ध और सरल विधि है।

संबंधित मुद्दे