दिलचस्प पोस्ट
लॉकऑब्जेक्ट पर सिंक्रनाइज़ और लॉक के रूप में इसका इस्तेमाल करने में क्या अंतर है? गिट: एक ही बार में कई शाखाओं को कैसे दुबारा बांटने के लिए? स्कैरी बहुत बुनियादी उदाहरण JavaFX2 में कार्य के बीच प्रगति सूचक को रीसेट कैसे करें? क्या संगतता पैकेज से जानबूझकर बाहर रखा गया था? जावा – रूपरेखा डॉलर राशि के रूप में डबल मान जावा को आकार 0 के सरणियों की अनुमति क्यों है? रिवर्स स्क्रोलिंग क्या संस्करण नियंत्रण के तहत रखा है? प्रीकंपेल्ड हेडर्स एपीके को उसी प्रमाणपत्र के साथ पिछले संस्करण के रूप में हस्ताक्षरित होना चाहिए Angular2 में सामान्य मेल सत्यापनकर्ता जर्सी REST का उपयोग करते हुए जावा primitives serialize कैसे करें कैसे ट्रिगर में स्ट्रिंग को जोड़ना मैं अपने एंड्रॉइड ऐप के रिज़ / कच्चे फ़ोल्डर में एमपी 3 कैसे खेलूं?

प्रदर्शन करनेवाला एक रिसाव का कारण हो सकता है क्योंकि इसकी चयनकर्ता अज्ञात है

मुझे एआरसी कंपाइलर द्वारा निम्नलिखित चेतावनी मिली है:

"performSelector may cause a leak because its selector is unknown". 

यहां मैं क्या कर रहा हूं:

 [_controller performSelector:NSSelectorFromString(@"someMethod")]; 

मुझे यह चेतावनी क्यों मिली? मैं समझता हूं कि कंपाइलर यह नहीं देख सकता है कि चयनकर्ता मौजूद है या नहीं, लेकिन यह एक रिसाव का कारण क्यों होगा? और मैं अपना कोड कैसे बदल सकता / सकती हूं ताकि मुझे यह चेतावनी अब और नहीं मिली?

Solutions Collecting From Web of "प्रदर्शन करनेवाला एक रिसाव का कारण हो सकता है क्योंकि इसकी चयनकर्ता अज्ञात है"

उपाय

संकलक एक कारण के लिए इस बारे में चेतावनी है यह बहुत दुर्लभ है कि इस चेतावनी को अनदेखा करना चाहिए, और इसके आसपास काम करना आसान है। ऐसे:

 if (!_controller) { return; } SEL selector = NSSelectorFromString(@"someMethod"); IMP imp = [_controller methodForSelector:selector]; void (*func)(id, SEL) = (void *)imp; func(_controller, selector); 

या अधिक दबंग (हालांकि पढ़ना मुश्किल और गार्ड के बिना):

 SEL selector = NSSelectorFromString(@"someMethod"); ((void (*)(id, SEL))[_controller methodForSelector:selector])(_controller, selector); 

व्याख्या

यहाँ क्या हो रहा है आप नियंत्रक से संबंधित विधि के लिए सी फंक्शन पॉइंटर के नियंत्रक से पूछ रहे हैं। सभी NSObject s पर प्रतिक्रिया के लिए methodForSelector: का methodForSelector: , लेकिन आप उद्देश्य-सी रनटाइम में class_getMethodImplementation भी उपयोग कर सकते हैं (यदि आपके पास केवल एक प्रोटोकॉल संदर्भ है, जैसे id<SomeProto> ) ये फ़ंक्शन पॉइंटर्स को IMP एस कहा जाता है, और साधारण typedef एड फ़ंक्शन पॉइंटर ( id (*IMP)(id, SEL, ...) ) 1 हैं । यह विधि के वास्तविक पद्धति के हस्ताक्षर के करीब हो सकता है, लेकिन वह बिल्कुल ठीक से मेल नहीं खाएगा।

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

अंत में, आप फ़ंक्शन पॉइंटर 2 को कॉल करते हैं

जटिल उदाहरण

जब चयनकर्ता तर्क लेता है या एक मूल्य देता है, तो आपको कुछ चीजें बदलनी होगी:

 SEL selector = NSSelectorFromString(@"processRegion:ofView:"); IMP imp = [_controller methodForSelector:selector]; CGRect (*func)(id, SEL, CGRect, UIView *) = (void *)imp; CGRect result = _controller ? func(_controller, selector, someRect, someView) : CGRectZero; 

चेतावनी के लिए तर्क

इस चेतावनी का कारण यह है कि एआरसी के साथ, रनटाइम को यह जानने की जरूरत है कि आप किस तरीके से फोन कर रहे हैं इसका नतीजा क्या होगा परिणाम कुछ भी हो सकता है: void , int , char , NSString * , id आदि। एआरसी सामान्यतः इस जानकारी को उस ऑब्जेक्ट प्रकार के शीर्ष लेख से प्राप्त करता है जिसे आप काम कर रहे हैं। 3

वास्तव में केवल 4 चीजें हैं जो एआरसी वापसी मान के लिए विचार करेंगे: 4

  1. गैर-ऑब्जेक्ट प्रकारों को अनदेखा करें ( void , int , आदि)
  2. ऑब्जेक्ट का मान बनाए रखें, तब रिलीज़ करें जब इसे अब उपयोग नहीं किया जाएगा (मानक धारणा)
  3. नई ऑब्जेक्ट मान को अब रिलीज़ नहीं किया ns_returns_retained ( init / copy family में विधियाँ या ns_returns_retained साथ ns_returns_retained )
  4. कुछ न करें और मान लें कि लौट गए ऑब्जेक्ट का मान स्थानीय क्षेत्र में मान्य होगा (जब तक भीतर का सबसे रिलीज पूल सूखा नहीं जाता है, ns_returns_autoreleased साथ श्रेय दिया जाता है)

विधि के लिए कॉल करने के लिए methodForSelector: मानता है कि जिस पद्धति पर कॉल किया जा रहा है उसका वापसी मान एक वस्तु है, लेकिन इसे बनाए नहीं रखता / जारी नहीं करता है। तो आप एक रिसाव का निर्माण कर सकते हैं यदि आपकी ऑब्जेक्ट को # 3 के ऊपर रिलीज करना माना जाता है (अर्थात, जिस पद्धति पर आप कॉल कर रहे हैं वह एक नया ऑब्जेक्ट देता है)।

चयनकर्ताओं के लिए आप उस void या अन्य गैर-वस्तुओं को कॉल करने का प्रयास कर रहे हैं, आप चेतावनी को अनदेखा करने के लिए कंपाइलर सुविधाओं को सक्षम कर सकते हैं, लेकिन यह खतरनाक हो सकता है। मैंने देखा है कि रीलैंक कुछ पुनरावृत्तियों से गुज़रता है कि यह उन बदले मूल्यों को कैसे नियंत्रित करता है जिन्हें स्थानीय चर को असाइन नहीं किया जाता है। एआरसी के साथ ऐसा कोई कारण नहीं है कि वह ऑब्जेक्ट मान को बनाए रखे और रिलीज़ नहीं कर सकता जो कि methodForSelector: से लौटा है। methodForSelector: भले ही आप इसे इस्तेमाल नहीं करना चाहते हैं संकलक के परिप्रेक्ष्य से, यह सभी के बाद एक वस्तु है इसका मतलब यह है कि यदि आप बुलाए जाने वाले विधि, कुछ विधि, एक गैर ऑब्जेक्ट ( void सहित) लौट रहा है, तो आप कचरा संकेतक मान को बनाए रख सकते हैं / जारी कर सकते हैं और क्रैश कर सकते हैं।

अतिरिक्त तर्क

एक विचार यह है कि यह एक ही चेतावनी है जिसमें प्रदर्शन के साथ performSelector:withObject: साथ- performSelector:withObject: और आप इस तरह की समस्याएं भी नहीं बता सकते हैं कि यह पद्धति कैसे मापदंडों की खपत करती है एआरसी खपत मापदंडों को घोषित करने की अनुमति देता है, और यदि विधि पैरामीटर की खपत करती है, तो आप अंततः एक ज़ोंबी और क्रैश के लिए एक संदेश भेज देंगे। इस तरह के आसपास ब्रैडड कास्टिंग के साथ काम करने के तरीके हैं, लेकिन वास्तव में यह केवल ऊपर IMP और फ़ंक्शन पॉइंटर पद्धति का उपयोग करना बेहतर होगा। खपत मापदंड शायद ही कभी एक मुद्दा है, यह ऊपर आने की संभावना नहीं है।

स्थिर चयनकर्ता

दिलचस्प बात यह है कि कंपाइलर स्थिर रूप से घोषित चयनकर्ताओं के बारे में शिकायत नहीं करेगा:

 [_controller performSelector:@selector(someMethod)]; 

इसका कारण यह है कि कंपाइलर वास्तव में संकलक के दौरान चयनकर्ता और वस्तु के बारे में सारी जानकारी रिकॉर्ड करने में सक्षम है। इसके बारे में किसी भी धारणा को बनाने की आवश्यकता नहीं है। (मैंने एक साल पहले इस स्रोत को देखकर इसकी जाँच की थी, लेकिन अभी इसका कोई संदर्भ नहीं है।)

दमन

ऐसी स्थिति के बारे में सोचने की कोशिश में जहां इस चेतावनी के दमन आवश्यक और अच्छा कोड डिजाइन होगा, मैं खाली आ रहा हूँ किसी को भी अगर वे एक अनुभव है, जहां इस चेतावनी silencing आवश्यक था साझा करें (और उपरोक्त बातें ठीक से संभाल नहीं करता है) कृपया।

अधिक

इसे NSMethodInvocation करने के लिए NSMethodInvocation का निर्माण करना भी संभव है, लेकिन ऐसा करने के लिए बहुत अधिक टाइपिंग की आवश्यकता होती है और धीमी भी होती है, इसलिए ऐसा करने के लिए बहुत कम कारण है

इतिहास

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

स्विफ्ट की शुरुआत के साथ, ऐप्पल ने प्रदर्शन- performSelector: "स्वाभाविक असुरक्षित" के रूप में परिवारों के परिवार को प्रलेखित किया है और वे स्विफ्ट के लिए उपलब्ध नहीं हैं।

समय के साथ, हमने यह प्रगति देखी है:

  1. उद्देश्य-सी के शुरुआती संस्करणों को performSelector: अनुमति दें performSelector: (मैनुअल मेमोरी प्रबंधन)
  2. एआरसी के साथ उद्देश्य- C performSelector: उपयोग के लिए चेतावनी देता है performSelector:
  3. स्विफ्ट को performSelector: पहुंच नहीं है performSelector: और इन विधियों को "स्वाभाविक रूप से असुरक्षित"

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


1 सभी उद्देश्य-सी विधियों में दो छिपी हुई तर्क, self और _cmd जो किसी विधि को कॉल करते समय निहित तरीके से जोड़े जाते हैं।

2 एक NULL फ़ंक्शन कॉल करना सी में सुरक्षित नहीं है। नियंत्रक की उपस्थिति की जांच करने वाला गार्ड यह सुनिश्चित करता है कि हमारे पास कोई ऑब्जेक्ट है इसलिए हम जानते हैं कि हम methodForSelector: से एक IMP प्राप्त करेंगे methodForSelector: (यद्यपि यह _objc_msgForward , संदेश फ़ॉरवर्डिंग सिस्टम में प्रविष्टि हो सकता है)। असल में, गार्ड की जगह के साथ, हमें पता है कि हमें कॉल करने के लिए एक फ़ंक्शन है।

3 वास्तव में, यदि आप id को ऑब्जेक्ट id रूप में घोषित करते हैं और आप सभी हेडर आयात नहीं कर रहे हैं, तो इसके लिए गलत जानकारी प्राप्त करना संभव है। आप कोड में दुर्घटनाओं को समाप्त कर सकते हैं कि कंपाइलर सोचता है कि ठीक है। यह बहुत दुर्लभ है, लेकिन हो सकता है। आम तौर पर आपको एक चेतावनी मिलती है कि यह नहीं पता है कि किस विधि से चयन करने के लिए दो विधि हस्ताक्षर हैं।

4 अधिक विवरणों के लिए बनाए गए रिटर्न वैल्यू और असंबद्ध रिटर्न वैल्यू पर एआरसी संदर्भ देखें।

एलएलवीएम 3.0 कंपाइलर में Xcode 4.2 में आप चेतावनी को निम्न प्रकार से दबा सकते हैं:

 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [self.ticketTarget performSelector: self.ticketAction withObject: self]; #pragma clang diagnostic pop 

यदि आपको कई स्थानों में त्रुटि मिल रही है, और प्रगामी को छुपाने के लिए सी मैक्रो सिस्टम का उपयोग करना चाहते हैं, तो आप चेतावनी को दबाने के लिए इसे आसान बनाने के लिए मैक्रो को परिभाषित कर सकते हैं:

 #define SuppressPerformSelectorLeakWarning(Stuff) \ do { \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \ Stuff; \ _Pragma("clang diagnostic pop") \ } while (0) 

आप इस तरह मैक्रो का उपयोग कर सकते हैं:

 SuppressPerformSelectorLeakWarning( [_target performSelector:_action withObject:self] ); 

यदि आपको निष्पादित संदेश के परिणाम की आवश्यकता है, तो आप यह कर सकते हैं:

 id result; SuppressPerformSelectorLeakWarning( result = [_target performSelector:_action withObject:self] ); 

इस बारे में मेरा अनुमान है: क्योंकि चयनकर्ता कंपाइलर के लिए अज्ञात है, एआरसी उचित मेमोरी प्रबंधन को लागू नहीं कर सकता है।

वास्तव में, ऐसे समय होते हैं जब एक विशिष्ट सम्मेलन द्वारा मेमोरी प्रबंधन विधि के नाम से जुड़ा हुआ हो। विशेष रूप से, मैं सुविधा निर्माणाकों की बनाम तरीके बनाने की सोच रहा हूं; कन्वेंशन द्वारा एक आटोरेलेज़ेड ऑब्जेक्ट पूर्व वापसी; उत्तरार्द्ध एक बनाए रखा वस्तु सम्मेलन चयनकर्ता के नाम पर आधारित है, इसलिए यदि संकलक चयनकर्ता को नहीं जानता है, तो वह उचित मेमोरी प्रबंधन नियम को लागू नहीं कर सकता है।

अगर यह सही है, तो मुझे लगता है कि आप सुरक्षित रूप से आपके कोड का उपयोग कर सकते हैं, बशर्ते आप सुनिश्चित करें कि स्मृति प्रबंधन के लिए सब कुछ ठीक है (जैसे कि आपके तरीके वे आवंटित नहीं करते हैं जो वे आवंटित करते हैं)।

अपनी प्रोजेक्ट बिल्ड सेटिंग्स में , अन्य चेतावनी फ्लैग्स ( WARNING_CFLAGS ) के तहत जोड़ें
-Wno-arc-performSelector-leaks

अब सिर्फ यह सुनिश्चित करें कि आप जिस चयनकर्ता को बुला रहे हैं वह आपके ऑब्जेक्ट को बनाए रखने या प्रतिलिपि नहीं करने का कारण बनता है।

जब तक कम्पाइलर चेतावनी को ओवरराइड करने की अनुमति नहीं देता तब तक एक वैकल्पिक हल के रूप में, आप रनटाइम का उपयोग कर सकते हैं

 objc_msgSend(_controller, NSSelectorFromString(@"someMethod")); 

के बजाय

 [_controller performSelector:NSSelectorFromString(@"someMethod")]; 

तुमको करना होगा

 #import <objc/message.h> 

केवल प्रदर्शन चयनकर्ता के साथ फाइल में त्रुटि को अनदेखा करने के लिए, #pragma को निम्नानुसार जोड़ें:

 #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 

यह इस लाइन पर चेतावनी को अनदेखा कर देगा, लेकिन फिर भी इसे अपनी बाकी परियोजना में अनुमति दें

अजीब लेकिन सच्चा: यदि स्वीकार्य (यानी परिणाम शून्य है और आप एक बार रनलोूप चक्र देकर दिमाग नहीं करते हैं), तो विलंब जोड़ें, भले ही यह शून्य है:

 [_controller performSelector:NSSelectorFromString(@"someMethod") withObject:nil afterDelay:0]; 

यह चेतावनी को निकाल देता है, संभवत: क्योंकि यह संकलक को आश्वस्त करता है कि कोई ऑब्जेक्ट नहीं लौटाया जा सकता है और किसी तरह गलत प्रबंधन किया जा सकता है।

ऊपर दिए गए उत्तर के आधार पर यहां एक अपडेट किया गया मैक्रो है। यह आपको एक रिटर्न स्टेटमेंट के साथ भी अपना कोड लपेटने की अनुमति देनी चाहिए।

 #define SUPPRESS_PERFORM_SELECTOR_LEAK_WARNING(code) \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \ code; \ _Pragma("clang diagnostic pop") \ SUPPRESS_PERFORM_SELECTOR_LEAK_WARNING( return [_target performSelector:_action withObject:self] ); 

इस कोड में संकलक झंडे या सीधे रनटाइम कॉल शामिल नहीं हैं:

 SEL selector = @selector(zeroArgumentMethod); NSMethodSignature *methodSig = [[self class] instanceMethodSignatureForSelector:selector]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; [invocation setSelector:selector]; [invocation setTarget:self]; [invocation invoke]; 

NSInvocation कई तर्कों को सेट करने की अनुमति देता है ताकि प्रदर्शन के विपरीत यह चयन किसी भी विधि पर काम करेगा।

खैर, यहां बहुत सारे उत्तर दिए गए हैं, लेकिन चूंकि यह थोड़ा अलग है, इसलिए मुझे लगता है कि मैं इसमें डालूंगा। मैं एक NSObject श्रेणी का उपयोग कर रहा हूं जो यह सुनिश्चित करने के लिए जांचता है कि चयनकर्ता को शून्य दिया जाता है, और कंपाइलर को भी दबा देता है चेतावनी।

 #import <Foundation/Foundation.h> #import <objc/runtime.h> #import "Debug.h" // not given; just an assert @interface NSObject (Extras) // Enforce the rule that the selector used must return void. - (void) performVoidReturnSelector:(SEL)aSelector withObject:(id)object; - (void) performVoidReturnSelector:(SEL)aSelector; @end @implementation NSObject (Extras) // Apparently the reason the regular performSelect gives a compile time warning is that the system doesn't know the return type. I'm going to (a) make sure that the return type is void, and (b) disable this warning // See http://stackoverflow.com/questions/7017281/performselector-may-cause-a-leak-because-its-selector-is-unknown - (void) checkSelector:(SEL)aSelector { // See http://stackoverflow.com/questions/14602854/objective-c-is-there-a-way-to-check-a-selector-return-value Method m = class_getInstanceMethod([self class], aSelector); char type[128]; method_getReturnType(m, type, sizeof(type)); NSString *message = [[NSString alloc] initWithFormat:@"NSObject+Extras.performVoidReturnSelector: %@.%@ selector (type: %s)", [self class], NSStringFromSelector(aSelector), type]; NSLog(@"%@", message); if (type[0] != 'v') { message = [[NSString alloc] initWithFormat:@"%@ was not void", message]; [Debug assertTrue:FALSE withMessage:message]; } } - (void) performVoidReturnSelector:(SEL)aSelector withObject:(id)object { [self checkSelector:aSelector]; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" // Since the selector (aSelector) is returning void, it doesn't make sense to try to obtain the return result of performSelector. In fact, if we do, it crashes the app. [self performSelector: aSelector withObject: object]; #pragma clang diagnostic pop } - (void) performVoidReturnSelector:(SEL)aSelector { [self checkSelector:aSelector]; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [self performSelector: aSelector]; #pragma clang diagnostic pop } @end 

भावी पीढ़ी के लिए, मैंने अपनी टोपी को अंगूठी में फेंकने का फैसला किया है 🙂

हाल ही में मैं target / selector प्रतिमान से अधिक से अधिक पुनर्गठन देख रहा हूं, जैसे कि प्रोटोकॉल, ब्लॉक, आदि के पक्ष में, हालांकि, प्रदर्शनकर्ता के लिए एक ड्रॉप-इन प्रतिस्थापन है जिसे मैंने कई बार उपयोग किया है :

 [NSApp sendAction: NSSelectorFromString(@"someMethod") to: _controller from: nil]; 

ये objc_msgSend() , एआरसी-सुरक्षित और objc_msgSend() लिए लगभग समान प्रतिस्थापन लगते हैं, बिना objc_msgSend() साथ बहुत कुछ किए।

यद्यपि, मुझे पता नहीं है कि आईओएस पर एनालॉग उपलब्ध है।

इस धागा पर मैट गैलोवे का उत्तर बताता है कि क्यों:

निम्नलिखित को धयान मे रखते हुए:

 id anotherObject1 = [someObject performSelector:@selector(copy)]; id anotherObject2 = [someObject performSelector:@selector(giveMeAnotherNonRetainedObject)]; 

अब, एआरसी कैसे जान सकता है कि पहला रिटर्न एक ऑब्जेक्ट के साथ 1 की संख्या को बनाए रखता है, लेकिन दूसरी ऑब्जेक्ट ऑटोरेलेज़ होने पर रिटर्न करता है?

ऐसा लगता है कि अगर आप रिटर्न वैल्यू को अनदेखा कर रहे हैं तो यह आमतौर पर चेतावनी को दबाने के लिए सुरक्षित है। मुझे यकीन नहीं है कि सबसे अच्छा अभ्यास क्या है यदि आपको वास्तव में एक निष्कासित ऑब्जेक्ट को निष्पादन से प्राप्त करने की आवश्यकता है – "ऐसा न करें" के अलावा

@ सी-सड़क समस्या विवरण के साथ सही लिंक प्रदान करता है । नीचे आप मेरा उदाहरण देख सकते हैं, जब चयनकर्ता मेमोरी रिसाव का कारण बनता है

 @interface Dummy : NSObject <NSCopying> @end @implementation Dummy - (id)copyWithZone:(NSZone *)zone { return [[Dummy alloc] init]; } - (id)clone { return [[Dummy alloc] init]; } @end void CopyDummy(Dummy *dummy) { __unused Dummy *dummyClone = [dummy copy]; } void CloneDummy(Dummy *dummy) { __unused Dummy *dummyClone = [dummy clone]; } void CopyDummyWithLeak(Dummy *dummy, SEL copySelector) { __unused Dummy *dummyClone = [dummy performSelector:copySelector]; } void CloneDummyWithoutLeak(Dummy *dummy, SEL cloneSelector) { __unused Dummy *dummyClone = [dummy performSelector:cloneSelector]; } int main(int argc, const char * argv[]) { @autoreleasepool { Dummy *dummy = [[Dummy alloc] init]; for (;;) { @autoreleasepool { //CopyDummy(dummy); //CloneDummy(dummy); //CloneDummyWithoutLeak(dummy, @selector(clone)); CopyDummyWithLeak(dummy, @selector(copy)); [NSThread sleepForTimeInterval:1]; }} } return 0; } 

एकमात्र तरीका है, जो मेरे उदाहरण में मेमोरी रिसाव का कारण है CopyDummyWithLeak इसका कारण यह है कि एआरसी नहीं जानता है, कि प्रतिलिपि चुने हुए ऑब्जेक्ट रिटर्न।

यदि आप मेमोरी लीक टूल चलाएंगे तो आप निम्न चित्र देख सकते हैं: यहां छवि विवरण दर्ज करें … और किसी अन्य मामले में कोई मेमोरी लीक नहीं है: यहां छवि विवरण दर्ज करें

स्कॉट थॉम्पसन मैक्रो अधिक सामान्य बनाने के लिए:

 // String expander #define MY_STRX(X) #X #define MY_STR(X) MY_STRX(X) #define MYSilenceWarning(FLAG, MACRO) \ _Pragma("clang diagnostic push") \ _Pragma(MY_STR(clang diagnostic ignored MY_STR(FLAG))) \ MACRO \ _Pragma("clang diagnostic pop") 

फिर इसे इस तरह प्रयोग करें:

 MYSilenceWarning(-Warc-performSelector-leaks, [_target performSelector:_action withObject:self]; ) 

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

चेतावनियों को दबाने मत करो!

कंपाइलर के साथ टिंकर करने के लिए 12 से कम वैकल्पिक समाधान नहीं हैं।
जब आप पहली बार कार्यान्वित होते समय चालाक होते हैं, तो पृथ्वी पर कुछ इंजीनियर आपके कदमों का पालन कर सकते हैं, और यह कोड अंततः टूट जाएगा।

सुरक्षित मार्ग:

ये सभी समाधान आपके मूल इरादे से कुछ भिन्नता के साथ काम करेंगे। मान लें कि यदि आप चाहें तो param nil हो सकता है:

सुरक्षित मार्ग, एक ही वैचारिक व्यवहार:

 // GREAT [_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:YES]; [_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:YES modes:@[(__bridge NSString *)kCFRunLoopDefaultMode]]; [_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:YES]; [_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:YES modes:@[(__bridge NSString *)kCFRunLoopDefaultMode]]; 

सुरक्षित मार्ग, थोड़ा अलग व्यवहार:

( यह प्रतिक्रिया देखें)
[NSThread mainThread] बदले किसी भी थ्रेड का उपयोग करें

 // GOOD [_controller performSelector:selector withObject:anArgument afterDelay:0]; [_controller performSelector:selector withObject:anArgument afterDelay:0 inModes:@[(__bridge NSString *)kCFRunLoopDefaultMode]]; [_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:NO]; [_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:NO]; [_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:NO modes:@[(__bridge NSString *)kCFRunLoopDefaultMode]]; [_controller performSelectorInBackground:selector withObject:anArgument]; [_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:NO]; [_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:NO modes:@[(__bridge NSString *)kCFRunLoopDefaultMode]]; 

खतरनाक मार्ग

कुछ प्रकार की कंपाइलर चुप्पी की आवश्यकता होती है, जो ब्रेक करने के लिए बाध्य है। ध्यान दें कि वर्तमान समय में, स्विफ्ट में इसे तोड़ दिया था

 // AT YOUR OWN RISK [_controller performSelector:selector]; [_controller performSelector:selector withObject:anArgument]; [_controller performSelector:selector withObject:anArgument withObject:nil]; 

Instead of using the block approach, which gave me some problems:

  IMP imp = [_controller methodForSelector:selector]; void (*func)(id, SEL) = (void *)imp; 

I will use NSInvocation, like this:

  -(void) sendSelectorToDelegate:(SEL) selector withSender:(UIButton *)button if ([delegate respondsToSelector:selector]) { NSMethodSignature * methodSignature = [[delegate class] instanceMethodSignatureForSelector:selector]; NSInvocation * delegateInvocation = [NSInvocation invocationWithMethodSignature:methodSignature]; [delegateInvocation setSelector:selector]; [delegateInvocation setTarget:delegate]; // remember the first two parameter are cmd and self [delegateInvocation setArgument:&button atIndex:2]; [delegateInvocation invoke]; } 

If you don't need to pass any arguments an easy workaround is to use valueForKeyPath . This is even possible on a Class object.

 NSString *colorName = @"brightPinkColor"; id uicolor = [UIColor class]; if ([uicolor respondsToSelector:NSSelectorFromString(colorName)]){ UIColor *brightPink = [uicolor valueForKeyPath:colorName]; ... } 

You could also use a protocol here. So, create a protocol like so:

 @protocol MyProtocol -(void)doSomethingWithObject:(id)object; @end 

In your class that needs to call your selector, you then have a @property.

 @interface MyObject @property (strong) id<MyProtocol> source; @end 

When you need to call @selector(doSomethingWithObject:) in an instance of MyObject, do this:

 [self.source doSomethingWithObject:object];