2012-02-13 14 views
8

अद्यतन बनाए रखने:AOP: प्रत्येक विधि में संदर्भ-अवगत कोड सम्मिलित करें, जबकि सूखी

कुछ महत्वपूर्ण सुझाव और आगे और पीछे जॉर्ज के साथ साथ , मैं करने के लिए दो अलग अलग तरीकों के साथ आ गए हैं प्राप्त वास्तव में क्या मैं CodeRunner में चाहते हैं और Github के सार साइट पर पोस्ट की गई: Objective-C AOP gist

कोड किसी न किसी तरह है, क्योंकि यह एक नई अवधारणा है और मैं सिर्फ 1:30 बजे समाप्त हो गया। यह निश्चित रूप से काम करता है और कुछ नब्बे हैं जैसे सभी विधियों को ऑटो-जोड़ना जो शुरुआती, गेटर्स या सेटर्स नहीं हैं। [अंत अद्यतन]

कई बार (लेकिन निश्चित रूप से अक्सर नहीं) मैं ऐसी परिस्थिति में आया हूं जहां मेरा कोड थोड़ा DRYer होगा यदि मैं कक्षा में प्रत्येक विधि के लिए कोड के संदर्भ-संवेदनशील टुकड़े को कॉल कर सकता हूं । उद्देश्य-सी रनटाइम का उपयोग पूरी तरह से ठीक है, मैं सी या सी ++ समाधान भी स्वीकार करता हूं।

बजाय:

- (void)methodName1 
{ 
    self->selector = _cmd; 
    NSLog(@"This method is named: %@",_cmd); 
    //more code 
} 

- (void)methodName2 
{ 
    self->selector = _cmd; 
    NSLog(@"This method is named: %@",_cmd); 
    //more code 
} 

परिणाम एक ही होने के साथ कुछ इस तरह है:

+ (void)AOPMethod 
{ 
    self->selector = _cmd; 
    NSLog(@"This method is named: %@",_cmd); 
} 

- (void)methodName1 
{ 
    //more code 
} 

- (void)methodName2 
{ 
    //more code 
} 

एक वास्तविक दुनिया आवेदन में, AOPMethod अधिक कोड होते हैं और वहाँ हो जाएगा कक्षा में और अधिक तरीके।

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

+0

क्या आप सिर्फ प्रवेश/बाहर निकलने के लिए प्रयास कर रहे हैं? –

+0

@GeorgFritzsche प्रश्न के लिए धन्यवाद। यदि कोई विधि अवरोध प्लेटफ़ॉर्म-अज्ञेयवादी है और विधि-स्तर संदर्भ-संवेदनशील डेटा जैसे _cmd को कक्षा के प्रत्येक विधि के भीतर उपयोग करने की अनुमति देगा-और कोड डुप्लिकेशन के बिना-फिर हां। दूसरे शब्दों में, यदि आप उल्लिखित मूल भाषाओं में से किसी एक में तकनीक साझा कर सकते हैं (और सभी प्लेटफार्मों पर ढांचे उपलब्ध नहीं हैं) जो बाद के उदाहरण के कुछ संस्करण को पूर्व के समान परिणाम देने की अनुमति देगा, जवाब। एक बार फिर धन्यवाद। –

+0

आपके विशिष्ट उपयोग-मामले (समान हस्ताक्षर वाले सभी विधियों) के लिए [इस दृष्टिकोण] (http://stackoverflow.com/questions/9242571/copy-a-method-imp-for-multiple-method-wwizzles) हो सकता है विधि सूची में सभी उपयुक्त तरीकों को पैच करने के लिए बढ़ाया गया। फिलहाल मैं एक सुरुचिपूर्ण अधिक सामान्य समाधान, केवल अक्षम लोगों के बारे में नहीं सोच सकता। –

उत्तर

5

प्रश्न में विशिष्ट उपयोग-मामले के लिए, कोई एक हैंडलर प्रदान कर सकता है जो मूल कार्यान्वयन कार्यों को प्रतिस्थापित करता है और हैंडलर के पहले/बाद में कॉल करता है साथ ही this approach जैसे कुछ का उपयोग करके मूल कार्य करता है। सामान्य रूप से विधि कार्यान्वयन पैचिंग काम नहीं करेगी क्योंकि प्रत्येक को प्रत्येक अवरुद्ध विधि हस्ताक्षर के लिए एक हैंडलर/अवरोध विधि प्रदान करना होगा।

अधिक सामान्य काम करेगा (यानी परिवर्तनीय तर्क कार्यों को छोड़कर सबकुछ के लिए) -forwardInvocation: को संभाला जाएगा। यद्यपि यहां समस्या यह है कि हमें उस विधि को पहली जगह में शामिल करना होगा। चूंकि हम ObjC2 में विधियों को नहीं हटा सकते हैं, जो जगह में नहीं किए जा सकते हैं।

हालांकि किया जा सकता है जो प्रॉक्सी का उपयोग कर रहा है जो forwardInvocation: को लागू करता है और हमारे पहले/बाद वाले हैंडलर को कॉल करता है।

@interface AspectProxy : NSProxy { 
    id target_; 
} 
- (id)initWithTarget:(id)target; 
@end 

@implementation AspectProxy 
- (id)initWithTarget:(id)target { 
    target_ = [target retain]; 
    return self; 
} 
- (void)dealloc { 
    [target_ release]; 
    [super dealloc]; 
} 
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { 
    return [target_ methodSignatureForSelector:sel]; 
} 
- (void)forwardInvocation:(NSInvocation *)inv { 
    SEL sel = [inv selector]; 
    NSLog(@"forwardInvocation for: %@", NSStringFromSelector(sel)); 
    if (sel == @selector(aspectBefore:) || sel == @selector(aspectAfter:)) { 
     return; 
    } 
    if ([target_ respondsToSelector:@selector(aspectBefore:)]) { 
     [target_ performSelector:@selector(aspectBefore:) withObject:inv]; 
    } 
    [inv invokeWithTarget:target_]; 
    if ([target_ respondsToSelector:@selector(aspectAfter:)]) { 
     [target_ performSelector:@selector(aspectAfter:) withObject:inv]; 
    } 
} 
@end 

हम एक init विधि से वास्तविक उदाहरण वापस जाने के लिए की जरूरत नहीं है के रूप में, यह भी पारदर्शी रूप से किया जा सकता है:

@interface Test : NSObject 
- (void)someFunction; 
@end 

@implementation Test 
- (id)init { 
    if (self = [super init]) { 
     return [[AspectProxy alloc] initWithTarget:[self autorelease]]; 
    } 
    return self; 
} 
- (void)aspectBefore:(NSInvocation *)inv { 
    NSLog(@"before %@", NSStringFromSelector([inv selector])); 
} 
- (void)aspectAfter:(NSInvocation *)inv { 
    NSLog(@"after %@", NSStringFromSelector([inv selector])); 
} 
- (void)someFunction { 
    NSLog(@"some function called"); 
} 
@end 

अब निम्नलिखित कोड:

Test *x = [[[Test alloc] init] autorelease]; 
[x someFunction]; 

। ..प्रिंट होगा:

forwardInvocation के लिए: someFunction
someFunction
कुछ समारोह
कहा जाता है से पहले someFunction

के बाद

एक runnable नमूना this gist में पाया जा सकता।

+1

यह वास्तव में एक अच्छा समाधान है। मैंने एक बार ऐसा प्रतिनिधि प्रॉक्सी बनाने के लिए ऐसा किया जो मुझे वैकल्पिक तरीकों के लिए 'अगर ([प्रतिनिधि उत्तर देने के लिए चयनकर्ता]] {} 'लिखने से बचने देता है। बहुत अच्छा काम करता है! – Alex

+0

मैंने फॉरवर्ड इनवोकेशन और एनएसपीरोक्सी का उपयोग विकल्पों में से एक के रूप में किया था (जिसे मैंने प्रश्न पोस्ट करने के कुछ ही समय बाद सीखा था), लेकिन रनटाइम का उपयोग करने के साथ और अधिक जुनूनी थी, इसलिए मैं इसे करने के लिए भी एक तरीका आया। दोनों तरीकों को मेरे प्रश्न में गिस्ट में जोड़ा गया है। मुझे लगता है कि आपका एनएसपीरोक्सी संस्करण कितना साफ है। –