2012-10-22 14 views
9

मुझे आईओएस ऐप में एक विशिष्ट सी लाइब्रेरी के लिए सी-स्टाइल कॉलबैक प्रदान करना होगा। कॉलबैक में void *userData या कुछ समान नहीं है। तो मैं एक संदर्भ में लूप करने में सक्षम नहीं हूँ। मैं इसे हल करने के लिए वैश्विक संदर्भ पेश करने से बचना चाहता हूं। एक आदर्श समाधान एक उद्देश्य-सी ब्लॉक होगा।क्या ऑब्जेक्टिव ब्लॉक को फ़ंक्शन पॉइंटर में लपेटने का कोई तरीका है?

मेरा प्रश्न: क्या किसी फंक्शन पॉइंटर में ब्लॉक को 'कास्ट' करने या किसी भी तरह लपेटने/लपेटने का कोई तरीका है?

उत्तर

6

तकनीकी रूप से, आप ब्लॉक के लिए फ़ंक्शन पॉइंटर तक पहुंच प्राप्त कर सकते हैं। लेकिन ऐसा करने के लिए यह पूरी तरह से असुरक्षित है, इसलिए मैं निश्चित रूप से इसकी अनुशंसा नहीं करता हूं। देखने के लिए कैसे, निम्न उदाहरण पर विचार करें:

#import <Foundation/Foundation.h> 

struct Block_layout { 
    void *isa; 
    int flags; 
    int reserved; 
    void (*invoke)(void *, ...); 
    struct Block_descriptor *descriptor; 
}; 

int main(int argc, char *argv[]) { 
    @autoreleasepool { 
     // Block that doesn't take or return anything 
     void(^block)() = ^{ 
      NSLog(@"Howdy %i", argc); 
     }; 

     // Cast to a struct with the same memory layout 
     struct Block_layout *blockStr = (struct Block_layout *)(__bridge void *)block; 

     // Now do same as `block()': 
     blockStr->invoke(blockStr); 




     // Block that takes an int and returns an int 
     int(^returnBlock)(int) = ^int(int a){ 
      return a; 
     }; 

     // Cast to a struct with the same memory layout 
     struct Block_layout *blockStr2 = (struct Block_layout *)(__bridge void *)returnBlock; 

     // Now do same as `returnBlock(argc)': 
     int ret = ((int(*)(void*, int a, ...))(blockStr2->invoke))(blockStr2, argc); 
     NSLog(@"ret = %i", ret); 
    } 
} 

चल रहा है कि पैदावार:

Howdy 1 
ret = 1 

कौन सा है कि हम क्या विशुद्ध रूप से block() के साथ सीधे उन ब्लॉकों को क्रियान्वित करने से उम्मीद थी। तो, आप अपने फ़ंक्शन पॉइंटर के रूप में invoke का उपयोग कर सकते हैं।

लेकिन जैसा कि मैंने कहा, यह पूरी तरह से असुरक्षित है। वास्तव में इसका उपयोग न करें!

आप क्या कह रहे हैं करने के लिए एक रास्ते से एक लेख को देखने के लिए चाहते हैं, तो इस बाहर की जाँच: http://www.mikeash.com/pyblog/friday-qa-2010-02-12-trampolining-blocks-with-mutable-code.html

यह आपको बस इतना करना होगा का सिर्फ एक महान लेख है इसे काम करने के लिए। अफसोस की बात है, यह कभी भी आईओएस पर काम नहीं करेगा (क्योंकि आपको किसी पृष्ठ को निष्पादन योग्य के रूप में चिह्नित करने की आवश्यकता है जिसे आपको अपने ऐप के सैंडबॉक्स में करने की अनुमति नहीं है)। लेकिन फिर भी, एक महान लेख।

+0

कृपया मेरी अज्ञानता से क्षमा करें, लेकिन यह वास्तव में असुरक्षित क्यों है? मुझे लगता है कि ऐसा इसलिए है क्योंकि संरचना एक आंतरिक है और भविष्य में बदल सकती है, है ना? –

+0

यह सही है :-)। – mattjgalloway

+0

इसके साथ समस्या यह है कि आपको अभी भी ब्लॉक को इनवॉक फ़ंक्शन में पास करने की आवश्यकता है, जबकि ओपी का कहना है कि आप कॉलबैक में कोई संदर्भ नहीं दे सकते हैं। – newacct

4

यदि आपके ब्लॉक को संदर्भ जानकारी की आवश्यकता है, और कॉलबैक कोई संदर्भ नहीं देता है, तो मुझे डर है कि उत्तर स्पष्ट है। ब्लॉक को कहीं भी संदर्भ जानकारी संग्रहित करनी होती है, इसलिए आप कभी भी इस तरह के ब्लॉक को नो-तर्क फ़ंक्शन पॉइंटर में डालने में सक्षम नहीं होंगे।

एक सावधानीपूर्वक डिजाइन किए गए वैश्विक परिवर्तनीय दृष्टिकोण शायद इस मामले में सबसे अच्छा समाधान है।

1

MABlockClosure बिल्कुल यह कर सकता है। लेकिन जो भी आपको चाहिए, उसके लिए यह अधिक हो सकता है।

0

मुझे पता है कि यह हल हो गया है, लेकिन इच्छुक पार्टियों के लिए, मेरे पास एक और समाधान है।

पूरे फ़ंक्शन को एक नए पता स्थान पर रीमेप करें। नए परिणामी पते को आवश्यक डेटा की कुंजी के रूप में उपयोग किया जा सकता है।

#import <mach/mach_init.h> 
#import <mach/vm_map.h> 

void *remap_address(void* address, int page_count) 
{ 
    vm_address_t source_address = (vm_address_t) address; 
    vm_address_t source_page = source_address & ~PAGE_MASK; 

    vm_address_t destination_page = 0; 
    vm_prot_t cur_prot; 
    vm_prot_t max_prot; 
    kern_return_t status = vm_remap(mach_task_self(), 
           &destination_page, 
           PAGE_SIZE*(page_count ? page_count : 4), 
           0, 
           VM_FLAGS_ANYWHERE, 
           mach_task_self(), 
           source_page, 
           FALSE, 
           &cur_prot, 
           &max_prot, 
           VM_INHERIT_NONE); 

    if (status != KERN_SUCCESS) 
    { 
     return NULL; 
    } 

    vm_address_t destination_address = destination_page | (source_address & PAGE_MASK); 

    return (void*) destination_address; 
} 

कि अब और आवश्यकता नहीं है पृष्ठों और ध्यान दें कि यह MABlockClosure से मंगलाचरण प्रति एक बहुत अधिक स्मृति लेता है याद रखें।

(आईओएस पर परीक्षण)

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