उद्देश्य सी। अभ्यास। घटनाओं

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

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

यह आलेख प्रोग्रामर के लिए अभिप्रेत है, जिन्हें ऑब्जेक्टिव-सी पर एप्लिकेशन बनाने में कुछ अनुभव है और वे पहले से ही समान बाइक लिख चुके हैं, या मानक तरीकों से समान समस्याओं को हल कर चुके हैं। यह विकल्प एक चांदी की गोली नहीं है, लेकिन यह एक सुविधाजनक तंत्र साबित हुआ है जो विभिन्न इंटरफेस और मापदंडों के साथ घटनाओं के सेट को संसाधित करने के लिए कोड के लेखन को कम करता है।

कक्षा का विचार काफी सरल है - इसमें हस्ताक्षरकर्ताओं की एक सूची शामिल है, जिनमें से प्रत्येक तत्व में दो घटक होते हैं - लक्ष्य और चयनकर्ता।

यह बाइक किस लिए बनाई गई है? यह मुझे लग रहा था कि घटनाओं के प्रसारण से संबंधित कुछ तर्क के लिए प्रस्तुत सभी मानक मॉडल की तुलना में यह अधिक सुविधाजनक है। शायद वह किसी को जीवन आसान बनाने में मदद करेगा।

.NET में, इवेंट लॉजिक का परिचित मॉडल दो मापदंडों के साथ एक प्रतिनिधि प्रदान करता है - प्रकार वस्तु का प्रेषक और EventAgs से विरासत में मिला प्रकार का आर्गन्स। हमारे दिमाग को नहीं तोड़ने के लिए, हम ऐसा ही करेंगे। सबसे पहले, एक खाली EventArgs वर्ग को परिभाषित करते हैं जिसमें से सभी घटना तर्क विरासत में मिलेंगे।

@interface AWEventArgs : NSObject @end 


अब हम एक ऐसे वर्ग को परिभाषित करते हैं, जिसमें "लक्ष्य ऑब्जेक्ट और कॉल की गई विधि" की एक जोड़ी होगी, जिसमें कुछ डिबगिंग जानकारी शामिल की जाएगी ताकि भविष्य में घटनाओं के तर्क को डीबग करना आसान हो।

 @interface AWEventHandler : NSObject { @private NSString *_description; } @property (nonatomic, assign) id target; @property (nonatomic, assign) SEL method; +(AWEventHandler *)handlerWithTarget:(id)target method:(SEL)method; @end @implementation AWEventHandler @synthesize method, target; -(id)initWithTarget:(id)t method:(SEL)m; { self = [super init]; if(self) { target = t; method = m; _description = [[NSString alloc] initWithFormat:@"EventHandler, Target=%@, Method=%@", NSStringFromClass([target class]), NSStringFromSelector(method)]; } return self; } -(NSString *)description { return _description; } -(void)dealloc { [_description release]; [super dealloc]; } +(AWEventHandler *)handlerWithTarget:(id)target method:(SEL)method { AWEventHandler *handler = [[[AWEventHandler alloc] initWithTarget:target method:method] autorelease]; return handler; } @end 


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

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

 @interface AWEventHandlersList : NSObject { NSMutableArray *_handlers; } @property (nonatomic, copy) NSString *name; -(void)addReceiver:(id)receiver delegate:(SEL)delegate; -(void)removeReceiver:(id)receiver delegate:(SEL)delegate; -(void)clearReceivers; -(void)invoke; -(void)invokeWithSender:(id)sender; -(void)invokeWithSender:(id)sender args:(AWEventArgs *)event; @property (nonatomic, retain) NSRunLoop *runLoop; @end 


मैं संक्षेप में बताऊंगा कि इस वर्ग के क्षेत्रों की आवश्यकता क्यों है।

पहला नाम है। मैं ईवेंट्स को नाम देना पसंद करता हूं ताकि आप लॉग्स में देख सकें कि किस इवेंट को ट्रिगर किया गया था। आमतौर पर, घटना के नाम के रूप में, मैं कक्षा के नाम के साथ-साथ उस पद्धति का नाम भी उपयोग करता हूं, जो विधि को बढ़ाने के लिए है। यह एक सुविधाजनक अभ्यास है, क्योंकि यह आपको इस घटना की तलाश में स्टैक को भयावह रूप से डकारने की अनुमति नहीं देता है, लेकिन डिबगिंग कंसोल में इस मान को देखें।

addReceiver और removeRecevier तार्किक हैं - वे एक ऑब्जेक्ट और एक चयनकर्ता को स्वीकार करते हैं जो भविष्य में कॉल प्राप्त करेंगे।

प्रसंस्करण के लिए हस्ताक्षरित वस्तुओं को पारित करके, invoke विधियों को एक घटना invoke चाहिए। उन्हें तीन संस्करणों में दिया जाता है - इस घटना में खाली शून्य मान संचारित नहीं करने के लिए कि घटना के कुछ मापदंडों की आवश्यकता नहीं है।

clearReceivers विधि आंतरिक है, इसे अनाम अनुभाग में परिभाषित करना बेहतर है, क्योंकि कॉलिंग कोड घटनाओं से अन्य वस्तुओं को अनसब्सक्राइब करने में सक्षम नहीं होना चाहिए, लेकिन ऐतिहासिक रूप से इसे इंटरफ़ेस में डाल दिया गया है। यह तय करना आसान है अगर यह आपको गलत लगता है।

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

एक वर्ग को लागू करना आदर्श रूप से तुच्छ है, लेकिन चयनकर्ताओं के काम करने की कुछ समझ की आवश्यकता है। मैं कोड में टिप्पणियों में कठिन बिंदुओं को स्पष्ट करूंगा।

 @implementation AWEventHandlersList @synthesize runLoop = _runLoop; @synthesize name = _name; -(id)init { self = [super init]; if(!self) return nil; _handlers = [[NSMutableArray alloc] init]; return self; } -(void)addReceiver:(id)receiver delegate:(SEL)delegate { /*    ,   ,          ,  *    .      */ [self removeReceiver:receiver delegate:delegate]; [_handlers addObject:[AWEventHandler handlerWithTarget:receiver method:delegate]]; } -(void)removeReceiver:(id)receiver delegate:(SEL)delegate { /*      ,    ,      * (NSLock),     ,            , *      ,         NSLock */ for(AWEventHandler *handler in [[_handlers copy] autorelease]) if(handler.method == delegate && handler.target == receiver) [_handlers removeObject:handler]; } -(void)clearReceivers { [_handlers removeAllObjects]; } -(void)invoke { [self invokeWithSender:nil args:nil]; } -(void)invokeWithSender:(id)sender { [self invokeWithSender:sender args:nil]; } -(void)invokeWithSender:(id)sender args:(AWEventArgs *)event { [self invokeWithSender:sender args:event runLoop:_runLoop]; } -(void)invokeWithSender:(id)sender args:(AWEventArgs *)event runLoop:(NSRunLoop *)runLoop { /*  ,         ,   null    *     */ if(!runLoop) runLoop = [NSRunLoop currentRunLoop]; NSUInteger order = 1; NSArray *handlersCopy = [NSArray arrayWithArray:_handlers]; for(AWEventHandler *handler in handlersCopy) if(runLoop == [NSRunLoop currentRunLoop]) [self internalInvoke:[NSArray arrayWithObjects:handler, sender == nil ? [NSNull null] : sender, event == nil ? [NSNull null] : event, nil]]; else [runLoop performSelector:@selector(internalInvoke:) target:self argument:[NSArray arrayWithObjects:handler, sender == nil ? [NSNull null] : sender, event == nil ? [NSNull null] : event, nil] order:order++ modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]]; } /*            performSelector:target:argument:order:modes: */ -(void)internalInvoke:(NSArray *)data { AWEventHandler *handler = [data objectAtIndex:0]; id sender = [data objectAtIndex:1]; if(sender == [NSNull null]) sender = nil; id args = [data objectAtIndex:2]; if(args == [NSNull null]) args = nil; /*               */ NSMethodSignature *mSig = [handler.target methodSignatureForSelector:handler.method]; if([mSig numberOfArguments] == 2) [handler.target performSelector:handler.method]; else if([mSig numberOfArguments] == 3) [handler.target performSelector:handler.method withObject:sender]; else if ([mSig numberOfArguments] == 4) [handler.target performSelector:handler.method withObject:sender withObject:args]; else @throw [NSException exceptionWithName:@"Invalid selector type" reason:@"This type of selector is not supported" userInfo:nil]; } -(void)dealloc { self.name = nil; [self clearReceivers]; [_handlers release]; [super dealloc]; } @end 


अब हम सहायक मैक्रो की एक जोड़ी को परिभाषित करते हैं जो हमें शाब्दिक रूप से दो पंक्तियों में कक्षा में घटनाओं के साथ काम करने के तर्क को एम्बेड करने का अवसर देगा।

 #define DEFINE_EVENT(eventName) \ -(void)add ## eventName ## Handler:(id)receiver action:(SEL)action; \ -(void)remove ## eventName ## Handler:(id)receiver action:(SEL)action #define DEFINE_EVENT_IMPL(eventName, innerVariable) \ -(void)add ## eventName ## Handler:(id)receiver action:(SEL)action \ { \ [innerVariable addReceiver:receiver delegate:action]; \ } \ \ -(void)remove ## eventName ## Handler:(id)receiver action:(SEL)action \ { \ [innerVariable removeReceiver:receiver delegate:action] ; \ } \ 


अब, कक्षा में एक घटना बनाने के लिए, आपको एक आंतरिक सूची चर को परिभाषित करने की आवश्यकता है:

 AWEventHandlersList *_handlers; 


इंटरफ़ेस में एक घटना को परिभाषित करें

 DEFINE_EVENT(Event); 


और सूची को किसी घटना से जोड़ते हैं

 DEFINE_EVENT_IMPL(Event, _handlers) 


दो तरीकों को स्वचालित रूप से वर्ग में जोड़ा जाता है - addEventHandler:action: और removeEventHandler:action: और आप _handlers ऑब्जेक्ट के _handlers विधियों के माध्यम से किसी घटना को ट्रिगर कर सकते हैं।

बेशक, यह मत भूलो कि _हैंडलर्स ऑब्जेक्ट को कंस्ट्रक्टर में आरंभीकृत किया जाना चाहिए

 _handlers = [AWEventHandlersList new]; 


और वस्तु विनाशक में नष्ट हो जाते हैं

 [_handlers release]; 


लेख के दूसरे भाग में मैं आपको बताऊंगा कि यह दृष्टिकोण किन समस्याओं की ओर ले जाता है और कैसे "मृत" लिंक की कठिनाइयों का सामना करना पड़ता है जो कि हमारी अपनी गलतियों के परिणामस्वरूप किसी भी अधिक या कम स्वैच्छिक अनुप्रयोग में उत्पन्न होते हैं।

Source: https://habr.com/ru/post/In168179/


All Articles