2009-10-24 14 views
18

में इंटरसेप्ट विधि कॉल उद्देश्य मैं उद्देश्य-सी में एक विधि कॉल को रोक सकता हूं? कैसे?उद्देश्य-सी

संपादित करें: मार्क पॉवेल के जवाब मुझे एक आंशिक समाधान, -forwardInvocation विधि दे दी है। लेकिन दस्तावेज़ीकरण बताता है कि --forwardInvocation केवल तभी बुलाया जाता है जब किसी ऑब्जेक्ट को एक संदेश भेजा जाता है जिसके लिए उसके पास कोई संबंधित विधि नहीं है। मुझे सभी परिस्थितियों में एक विधि कहा जाना चाहिए, भले ही रिसीवर के पास चयनकर्ता हो।

उत्तर

22

आप इसे विधि कॉल को तेज करके करते हैं।मान लिया जाये कि आप चाहते हैं NSTableView करने के लिए सभी रिलीज हड़पने के लिए:

static IMP gOriginalRelease = nil; 
static void newNSTableViewRelease(id self, SEL releaseSelector, ...) { 
    NSLog(@"Release called on an NSTableView"); 
    gOriginalRelease(self, releaseSelector); 
} 


    //Then somewhere do this: 
    gOriginalRelease = class_replaceMethod([NSTableView class], @selector(release), newNSTableViewRelease, "[email protected]:"); 

आप उद्देश्य सी क्रम documentation में अधिक जानकारी प्राप्त कर सकते हैं।

+0

आप शायद @ चयनकर्ता (रिलीज), @selector (dealloc) नहीं –

+0

हाँ, मेरा मतलब @ चयनकर्ता (रिलीज) था। यह मैं प्यारा और इसे कुछ कोड से चिपकाता हूं जो डेलोक को घुमाता है, लेकिन उदाहरण में ऐसा नहीं दिखाना चाहता था, क्योंकि ऐसा करने के साथ कुछ विशेष मुद्दे हैं। फिक्स्ड। –

+0

@LouisGerbarg क्या आप कृपया अपने मूल स्रोत के लिए एक लिंक पोस्ट कर सकते हैं जो रिलीज़ हो गया है? – funroll

-1

किसी विधि को कॉल करने पर कुछ करने के लिए, आप ईवेंट आधारित दृष्टिकोण को आजमा सकते हैं। तो जब विधि कहा जाता है, तो यह किसी घटना को प्रसारित करता है, जिसे किसी भी श्रोताओं द्वारा उठाया जाता है। मैं उद्देश्य सी के साथ अच्छा नहीं हूं, लेकिन मैंने कोको में NSNotificationCenter का उपयोग करके कुछ ऐसा ही पाया।

लेकिन यदि "अवरोध" से आपका मतलब "रोकना" है, तो हो सकता है कि आपको गीलेर को तय करने के लिए अधिक तर्क की आवश्यकता हो।

+2

मुझे लगता है कि वह मेटाप्रोग्रामिंग तकनीकों का जिक्र कर रहा है। – Geo

1

शायद आप NSObject की -forwardInvocation विधि चाहते हैं। यह आपको एक संदेश कैप्चर करने, इसे पुनः लक्षित करने और फिर इसे फिर से भेजने की अनुमति देता है।

+2

प्रलेखन बताता है कि * -forwardInvocation * केवल तभी बुलाया जाता है जब किसी ऑब्जेक्ट को एक संदेश भेजा जाता है जिसके लिए उसके पास कोई संबंधित विधि नहीं है। मुझे सभी परिस्थितियों में एक विधि कहा जाना चाहिए, भले ही रिसीवर के पास चयनकर्ता हो। – luvieere

+1

इस टिप्पणी में आपके मूल प्रश्न की तुलना में काफी अधिक जानकारी है ...;) – MarkPowell

+0

मैं संपादित करूँगा, इंगित करने के लिए धन्यवाद। – luvieere

0

एक विधि कॉल, नहीं। एक संदेश भेजता है, हां, लेकिन अगर आपको एक अच्छा जवाब चाहिए तो आपको बहुत अधिक वर्णनात्मक होना होगा।

+2

ओब्जे-सी में एक विधि कॉल के लिए शब्द "एक संदेश भेजना" है, इसलिए वे एक हैं और वे समान हैं, एक फ़ंक्शन कॉल के विपरीत - जो आप सही हैं - गतिशील रूप से अवरुद्ध नहीं किया जा सकता है। –

1

आप अपने आप में से किसी एक के साथ विधि कॉल को घुमा सकते हैं, जो आप "अवरोध" पर जो भी करना चाहते हैं और मूल कार्यान्वयन के माध्यम से कॉल करता है। Swizzling class_replaceMethod() के साथ किया जाता है।

15

उद्देश्य-सी में इंटरसेप्टिंग विधि कॉल (यह एक उद्देश्य-सी है, सी कॉल नहीं है) विधि स्विजलिंग नामक तकनीक के साथ किया जाता है।

आप here को कार्यान्वित करने के तरीके पर एक परिचय पा सकते हैं। उदाहरण के लिए, वास्तविक परियोजना में विधि स्विजलिंग कैसे लागू की जाती है, OCMock (उद्देश्य-सी के लिए एक अलगाव फ्रेमवर्क) देखें।

11

ऑब्जेक्टिव-सी में एक संदेश भेजा जा रहा है समारोह objc_msgSend(receiver, selector, arguments) या उसके संस्करण objc_msgSendSuper, objc_msgSend_stret, objc_msgSendSuper_stret में से एक के लिए एक कॉल में अनुवाद किया है।

यदि इन कार्यों के कार्यान्वयन को बदलना संभव था, तो हम किसी भी संदेश को रोक सकते थे। दुर्भाग्यवश, objc_msgSend उद्देश्य-सी रनटाइम का हिस्सा है और इसे ओवरराइड नहीं किया जा सकता है।

गुगल करके मुझे Google पुस्तकें पर एक पेपर मिला: A Reflective Architecture for Process Control Applications by Charlotte Pii Lunau। पेपर कस्टम मेटाऑब्जेक्ट क्लास के उदाहरण के लिए किसी ऑब्जेक्ट के isa क्लास पॉइंटर को रीडायरेक्ट करके एक हैक प्रस्तुत करता है। संशोधित ऑब्जेक्ट के लिए लक्षित संदेश इस प्रकार मेटाऑब्जेक्ट उदाहरण में भेजे गए हैं। चूंकि मेटा ऑब्जेक्ट क्लास के पास स्वयं का कोई तरीका नहीं है, इसलिए यह संशोधित ऑब्जेक्ट को संदेश अग्रेषित करके आगे के आमंत्रण का जवाब दे सकता है।

पेपर में स्रोत कोड के दिलचस्प बिट्स शामिल नहीं हैं और मुझे नहीं पता कि इस तरह के दृष्टिकोण कोको में दुष्प्रभाव होंगे। लेकिन यह कोशिश करना दिलचस्प हो सकता है।

2

NSProxy का एक उपवर्ग बनाएँ और -forwardInvocation: और -methodSignatureForSelector: लागू (या -forwardingTargetForSelector:, अगर आप बस उस पर एक दूसरी वस्तु के बजाय निर्देशन कर रहे हैं विधि अपने आप के साथ नगण्य)।

NSProxy-forwardInvocation: को लागू करने के लिए डिज़ाइन किया गया एक वर्ग है। इसमें कुछ विधियां हैं, लेकिन अधिकतर आप नहीं चाहते हैं कि उन्हें पकड़ा जाए। उदाहरण के लिए, संदर्भ गिनती विधियों को पकड़ने से प्रॉक्सी को कचरा संग्रह के अलावा छोड़ दिया जा सकता है। लेकिन अगर NSProxy पर विशिष्ट विधियां हैं जिन्हें आपको पूरी तरह से आगे बढ़ाना है, तो आप उस विधि को विशेष रूप से ओवरराइड कर सकते हैं और मैन्युअल रूप से -forwardInvocation: पर कॉल कर सकते हैं। ध्यान दें कि केवल NSProxy दस्तावेज़ीकरण के तहत एक विधि सूचीबद्ध है इसका मतलब यह नहीं है कि NSProxy इसे कार्यान्वित करता है, केवल यह अपेक्षा की जाती है कि सभी प्रॉक्सी ऑब्जेक्ट्स में यह है।

यदि यह आपके लिए काम नहीं करेगा, तो अपनी स्थिति के बारे में अतिरिक्त जानकारी प्रदान करें।

5

अगर आप लॉग इन करना चाहते हैं तो संदेश आपके एप्लिकेशन कोड से भेजता है, फॉरवर्डिंग टार्गेटफॉर चयनकर्ता: टिप समाधान का हिस्सा है।

@interface Interceptor : NSObject 
@property (nonatomic, retain) id interceptedTarget; 
@end 

@implementation Interceptor 
@synthesize interceptedTarget=_interceptedTarget; 

- (void)dealloc { 
    [_interceptedTarget release]; 
    [super dealloc]; 
} 

- (id)forwardingTargetForSelector:(SEL)aSelector { 
    NSLog(@"Intercepting %@", NSStringFromSelector(aSelector)); 
    return self.interceptedTarget; 
} 

@end 

अब कुछ इस तरह करते हैं:
अपने वस्तु लपेटें

Interceptor *i = [[[Interceptor alloc] init] autorelease]; 
NSFetchedResultsController *controller = [self setupFetchedResultsController]; 
i.interceptedTarget = controller; 
controller = (NSFetchedResultsController *)i; 

और आप एक लॉग संदेश भेजता है के होगा। नोट, अवरुद्ध वस्तु के भीतर से भेजे गए भेजों को अवरुद्ध नहीं किया जाएगा, क्योंकि उन्हें मूल ऑब्जेक्ट 'स्वयं' सूचक का उपयोग करके भेजा जाएगा।

3

आप केवल संदेशों को बाहर से बुलाया लॉग इन करना चाहते हैं (आमतौर पर प्रतिनिधियों से कहा जाता है, जो संदेशों के प्रकार, जब, आदि को देखने के लिए), तो आप इस तरह respondsToSelector ओवरराइड कर सकते हैं:

- (BOOL)respondsToSelector:(SEL)aSelector { 
NSLog(@"respondsToSelector called for '%@'", NSStringFromSelector(aSelector)); 

// look up, if a method is implemented 
if([[self class] instancesRespondToSelector:aSelector]) return YES; 

return NO; 

}