डमियों के लिए NSInvocation?


139

कैसे NSInvocationकाम करता है ? क्या कोई अच्छा परिचय है?

मुझे विशेष रूप से यह समझने में समस्याएँ हो रही हैं कि निम्न कोड ( मैक ओएस एक्स, 3 डी संस्करण के लिए कोको प्रोग्रामिंग से ) कैसे काम करता है, लेकिन फिर भी ट्यूटोरियल नमूने के स्वतंत्र रूप से अवधारणाओं को लागू करने में सक्षम हो। कोड:

- (void)insertObject:(Person *)p inEmployeesAtIndex:(int)index
{
    NSLog(@"adding %@ to %@", p, employees);
    // Add inverse of this operation to undo stack
    NSUndoManager *undo = [self undoManager];
    [[undo prepareWithInvocationTarget:self] removeObjectFromEmployeesAtIndex:index];
    if (![undo isUndoing])
        [undo setActionName:@"Insert Person"];

    // Finally, add person to the array
    [employees insertObject:p atIndex:index];
}

- (void)removeObjectFromEmployeesAtIndex:(int)index
{
    Person *p = [employees objectAtIndex:index];
    NSLog(@"removing %@ from %@", p, employees);
    // Add inverse of this operation to undo stack
    NSUndoManager *undo = [self undoManager];
    [[undo prepareWithInvocationTarget:self] insertObject:p
                                       inEmployeesAtIndex:index];
    if (![undo isUndoing])
        [undo setActionName:@"Delete Person"];

    // Finally, remove person from array
    [employees removeObjectAtIndex:index];
}

मुझे वह मिलता है जो मैं करने की कोशिश कर रहा हूं। (Btw, employeesएक है NSArrayएक कस्टम के Personवर्ग।)

एक .NET पुरुष होने के नाते, मैं अपरिचित Obj-C और कोको अवधारणाओं को मोटे तौर पर अनुरूप .NET अवधारणाओं से संबद्ध करने का प्रयास करता हूं। क्या यह .NET की डेलिगेट अवधारणा के समान है, लेकिन अनकैप्ड है?

यह पुस्तक से 100% स्पष्ट नहीं है, इसलिए मैं असली कोको / ओबज-सी विशेषज्ञों से कुछ पूरक की तलाश में हूं, फिर से इस लक्ष्य के साथ कि मैं सरल (-इश) उदाहरण के नीचे मूल अवधारणा को समझता हूं। मैं वास्तव में स्वतंत्र रूप से ज्ञान को लागू करने में सक्षम होना चाहता हूं - अध्याय 9 तक, मुझे ऐसा करने में कोई कठिनाई नहीं हो रही थी। पर अब ...

अग्रिम में धन्यवाद!

जवाबों:


284

Apple के NSInvocation वर्ग संदर्भ के अनुसार :

एक NSInvocationऑब्जेक्टिव-सी मैसेज स्टैटिक रेंडर किया गया है, यानी यह एक एक्शन है जिसे ऑब्जेक्ट में बदल दिया गया है।

और, थोड़ा और विस्तार में:

संदेशों की अवधारणा उद्देश्य-सी दर्शन के लिए केंद्रीय है। जब भी आप किसी विधि को कॉल करते हैं, या किसी ऑब्जेक्ट के एक वैरिएबल तक पहुंचते हैं, तो आप इसे एक संदेश भेज रहे हैं। NSInvocationउस समय काम आता है जब आप किसी वस्तु को समय पर किसी भिन्न बिंदु पर संदेश भेजना चाहते हैं, या एक ही संदेश को कई बार भेजना चाहते हैं। NSInvocationआपको उस संदेश का वर्णन करने की अनुमति देता है जिसे आप भेजने जा रहे हैं, और फिर इसे लागू करें (वास्तव में इसे लक्ष्य वस्तु पर भेजें) बाद में।


उदाहरण के लिए, मान लें कि आप एक सरणी में एक स्ट्रिंग जोड़ना चाहते हैं। आप आम addObject:तौर पर इस प्रकार संदेश भेजेंगे :

[myArray addObject:myString];

अब, मान लें कि आप NSInvocationइस संदेश को किसी अन्य बिंदु पर भेजने के लिए उपयोग करना चाहते हैं :

सबसे पहले, आप एक तैयार करते थे NSInvocationसाथ प्रयोग के लिए वस्तु NSMutableArrayके addObject:चयनकर्ता:

NSMethodSignature * mySignature = [NSMutableArray
    instanceMethodSignatureForSelector:@selector(addObject:)];
NSInvocation * myInvocation = [NSInvocation
    invocationWithMethodSignature:mySignature];

इसके बाद, आप संदेश भेजने के लिए किस वस्तु को निर्दिष्ट करेंगे:

[myInvocation setTarget:myArray];

उस ऑब्जेक्ट पर भेजने के लिए इच्छित संदेश निर्दिष्ट करें:

[myInvocation setSelector:@selector(addObject:)];

और उस विधि के लिए किसी भी तर्क को भरें:

[myInvocation setArgument:&myString atIndex:2];

ध्यान दें कि ऑब्जेक्ट तर्कों को पॉइंटर द्वारा पारित किया जाना चाहिए। रायन मैककॉइग को धन्यवाद कि आप इसे इंगित करें, और अधिक जानकारी के लिए कृपया Apple के दस्तावेज देखें।

इस बिंदु पर, myInvocationएक पूर्ण वस्तु है, एक संदेश का वर्णन करता है जिसे भेजा जा सकता है। संदेश भेजने के लिए, आप कॉल करेंगे:

[myInvocation invoke];

यह अंतिम चरण संदेश भेजने का कारण होगा, अनिवार्य रूप से निष्पादित [myArray addObject:myString];

इसे एक ईमेल भेजने की तरह सोचें। आप एक नया ईमेल ( NSInvocationऑब्जेक्ट) खोलते हैं , उस व्यक्ति (ऑब्जेक्ट) के पते को भरें जिसे आप इसे भेजना चाहते हैं, प्राप्तकर्ता के लिए एक संदेश टाइप करें (निर्दिष्ट करेंselector और तर्क ), और फिर "भेजें" पर क्लिक करें (कॉल करें) invoke)।

अधिक जानकारी के लिए NSInvocation का उपयोग करना देखें । NSInvocation का उपयोग करना देखें यदि ऊपर काम नहीं कर रहा है।


NSUndoManagerNSInvocationवस्तुओं का उपयोग करता है ताकि यह आदेशों को उलट सके। अनिवार्य रूप से, आप जो कर रहे हैं, वह NSInvocationकहने के लिए एक ऑब्जेक्ट बना रहा है : "अरे, यदि आप जो मैंने अभी किया है उसे पूर्ववत करना चाहते हैं, तो इन तर्कों के साथ, उस ऑब्जेक्ट पर यह संदेश भेजें"। आप NSInvocationऑब्जेक्ट को देते हैं NSUndoManager, और यह उस ऑब्जेक्ट को अयोग्य कार्यों की एक सरणी में जोड़ता है। यदि उपयोगकर्ता "पूर्ववत करें" कहता है, तो NSUndoManagerबस सरणी में सबसे हाल की कार्रवाई देखता है, और संग्रहीत NSInvocationऑब्जेक्ट को आवश्यक कार्रवाई करने के लिए आमंत्रित करता है।

अधिक विवरण के लिए पूर्ववत संचालन रजिस्टर देखें ।


10
अन्यथा उत्कृष्ट उत्तर के लिए एक मामूली सुधार ... आपको ऑब्जेक्ट में एक सूचक को पास करना होगा setArgument:atIndex:, इसलिए आर्ग असाइनमेंट को वास्तव में पढ़ना चाहिए [myInvocation setArgument:&myString atIndex:2]
रयान मैककियुग

60
बस रयान के नोट को स्पष्ट करने के लिए, इंडेक्स 0 "स्व" के लिए आरक्षित है और इंडेक्स 1 "_cmd" के लिए आरक्षित है (लिंक को देखें। अधिक विवरण के लिए पोस्ट किए गए नाम)। इसलिए आपका पहला तर्क 2 इंडेक्स पर रखा गया, दूसरा तर्क 3 इंडेक्स पर, आदि ...
डेव

4
@haroldcampbell: हमें क्या कॉल करना है?
ई.जेम्स

6
मुझे समझ नहीं आ रहा है कि हमें सेट सेलेक्टर को क्यों बुलाना है, क्योंकि हमने पहले ही चयनकर्ता को mySignature में निर्दिष्ट कर दिया है।
ग्लेनो

6
@Geno: NSInvocation काफी लचीली है। आप वास्तव में कोई भी चयनकर्ता सेट कर सकते हैं जो विधि हस्ताक्षर से मेल खाता है, इसलिए आपको जरूरी नहीं कि उसी चयनकर्ता का उपयोग किया जाए जो विधि हस्ताक्षर बनाने के लिए उपयोग किया गया था। इस उदाहरण में, आप आसानी से setSelector कर सकते हैं: @selector (removeObject :), क्योंकि वे एक ही विधि हस्ताक्षर साझा करते हैं।
ई। जम्स 27'13

48

यहाँ कार्रवाई में NSInvocation का एक सरल उदाहरण है:

- (void)hello:(NSString *)hello world:(NSString *)world
{
    NSLog(@"%@ %@!", hello, world);

    NSMethodSignature *signature  = [self methodSignatureForSelector:_cmd];
    NSInvocation      *invocation = [NSInvocation invocationWithMethodSignature:signature];

    [invocation setTarget:self];                    // index 0 (hidden)
    [invocation setSelector:_cmd];                  // index 1 (hidden)
    [invocation setArgument:&hello atIndex:2];      // index 2
    [invocation setArgument:&world atIndex:3];      // index 3

    // NSTimer's always retain invocation arguments due to their firing delay. Release will occur when the timer invalidates itself.
    [NSTimer scheduledTimerWithTimeInterval:1 invocation:invocation repeats:NO];
}

जब कहा जाता है - [self hello:@"Hello" world:@"world"];विधि होगी:

  • प्रिंट "नमस्ते दुनिया!"
  • अपने लिए एक NSMethodSignature बनाएँ।
  • अपने आप को कॉल करते हुए, NSInvocation को बनाएं और आबाद करें।
  • एक NSTimer को NSInvocation पास करें
  • टाइमर (लगभग) 1 सेकंड में आग लगाएगा, जिससे विधि को अपने मूल तर्कों के साथ फिर से बुलाया जाएगा।
  • दोहराएँ।

अंत में, आपको एक प्रिंटआउट मिलेगा जैसे:

2010-07-11 17:48:45.262 Your App[2523:a0f] Hello world!
2010-07-11 17:48:46.266 Your App[2523:a0f] Hello world!
2010-07-11 17:48:47.266 Your App[2523:a0f] Hello world!
2010-07-11 17:48:48.267 Your App[2523:a0f] Hello world!
2010-07-11 17:48:49.268 Your App[2523:a0f] Hello world!
2010-07-11 17:48:50.268 Your App[2523:a0f] Hello world!
2010-07-11 17:48:51.269 Your App[2523:a0f] Hello world!
...

निश्चित रूप से, selfNSTnvocation को इसमें भेजने के लिए NSTimer के लिए लक्ष्य ऑब्जेक्ट मौजूद होना चाहिए। उदाहरण के लिए, एक सिंगलटन ऑब्जेक्ट, या एक AppDelegate जो अनुप्रयोग की अवधि के लिए मौजूद है।


अपडेट करें:

जैसा कि ऊपर उल्लेख किया गया है, जब आप NSTimer के तर्क के रूप में एक NSInvocation पास करते हैं, NSTimer स्वतः NSInvocation के सभी तर्कों को बरकरार रखता है।

यदि आप एक NSTner के तर्क के रूप में एक NSInvocation पास नहीं कर रहे हैं, और इसे थोड़ी देर के लिए चिपकाए जाने पर योजना बनाते हैं, तो आपको इसकी -retainArgumentsविधि को कॉल करना होगा । अन्यथा इसके तर्कों को मंगलाचरण से पहले निपटाया जा सकता है, आखिरकार आपका कोड दुर्घटनाग्रस्त हो सकता है। यह कैसे करना है:

NSMethodSignature *signature  = ...;
NSInvocation      *invocation = [NSInvocation invocationWithMethodSignature:signature];
id                arg1        = ...;
id                arg2        = ...;

[invocation setTarget:...];
[invocation setSelector:...];
[invocation setArgument:&arg1 atIndex:2];
[invocation setArgument:&arg2 atIndex:3];

[invocation retainArguments];  // If you do not call this, arg1 and arg2 might be deallocated.

[self someMethodThatInvokesYourInvocationEventually:invocation];

6
यह दिलचस्प है कि भले ही invocationWithMethodSignature:शुरुआती का उपयोग किया जाता है, फिर भी आपको कॉल करने की आवश्यकता है setSelector:। यह बेमानी लगता है, लेकिन मैंने अभी परीक्षण किया है और यह आवश्यक है।
थॉमसडब्ल्यू

क्या यह अनंत पाश में चलता रहता है? और क्या है _cmd
j2emanue


0

मैं NSInvocation के साथ विभिन्न विधि प्रकारों को कॉल करने का एक सरल उदाहरण बनाता हूं।

मुझे obj_msgSend का उपयोग करके कई पैरामेट्स को कॉल करने में समस्या थी

https://github.com/clearbrian/NSInvocation_Runtime

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.