मैं एक "कोड इंजेक्टर कक्षा" को लागू करने रहा हूँ, उस विधि swizzling के माध्यम से आप इस तरह कुछ करने के लिए संभावना दे सकते हैं:चर तर्क के साथ एक विधि Swizzling और संदेश को अग्रेषित - बुरा पहुँच
FLCodeInjector *injector = [FLCodeInjector injectorForClass:[self class]];
[injector injectCodeBeforeSelector:@selector(aSelector:) code:^{
NSLog(@"This code should be injected");
}];
aSelector
तर्कों की परिवर्तनीय संख्या के साथ एक विधि हो सकती है, और परिवर्तनीय वापसी प्रकार। तर्क/और वापसी प्रकार वस्तुओं या आदिम प्रकार हो सकता है।
पहले, मैं तुम्हें समझ में मैं क्या कर रहा हूँ जाने के लिए injectCodeBeforeSelector:
का कोड देते हैं (मैं कोड के दिलचस्प भागों नहीं निकाला गया):
- (void)injectCodeBeforeSelector:(SEL)method code:(void (^)())completionBlock
{
NSString *selector = NSStringFromSelector(method);
[self.dictionaryOfBlocks setObject:completionBlock forKey:selector];
NSString *swizzleSelector = [NSString stringWithFormat:@"SWZ%@", selector];
// add a new method to the swizzled class
Method origMethod = class_getInstanceMethod(self.mainClass, NSSelectorFromString(selector));
const char *encoding = method_getTypeEncoding(origMethod);
[self addSelector:NSSelectorFromString(swizzleSelector) toClass:self.mainClass methodTypeEncoding:encoding];
SwizzleMe(self.mainClass, NSSelectorFromString(selector), NSSelectorFromString(swizzleSelector));
}
-(void)addSelector:(SEL)selector toClass:(Class)aClass methodTypeEncoding:(const char *)encoding
{
class_addMethod(aClass,
selector,
(IMP)genericFunction, encoding);
}
असल में, मैं class_addMethod का उपयोग नकली जोड़ने के लिए/एक प्रकार का शराबी गंतव्य वर्ग के लिए विधि, फिर तेजस्वी करो। विधि के क्रियान्वयन इस तरह एक समारोह के लिए सेट है:
id genericFunction(id self, SEL cmd, ...) {
// call the block to inject
...
// now forward the message to the right method, since method are swizzled
// I need to forward to the "fake" selector SWZxxx
NSString *actualSelector = NSStringFromSelector(cmd);
NSString *newSelector = [NSString stringWithFormat:@"SWZ%@", actualSelector];
SEL finalSelector = NSSelectorFromString(newSelector);
// forward the argument list
va_list arguments;
va_start (arguments, cmd);
return objc_msgSend(self, finalSelector, arguments);
}
अब समस्या: मैं अंतिम पंक्ति पर एक EXC_BAD_INSTRUCTION (objc_msgSend_corrupt_cache_error()) है। समस्या तब होती है जब मैं नकली चयनकर्ता पर va_list तर्क को अग्रेषित करता हूं। अगर मैं
return objc_msgSend(self, cmd, arguments);
के अंतिम पंक्ति कोई त्रुटि है, लेकिन स्पष्ट रूप से एक अनंत प्रत्यावर्तन शुरू होता है बदल जाते हैं।
मैं करने के लिए कोशिश की है:
- उपयोग va_copy
- संदेश
लेकिन कोई परिणाम नहीं भेजने से पहले एक प्रकार का शराबी को हटा दें। मुझे लगता है कि समस्या इस तथ्य से संबंधित है: va_list एक साधारण सूचक नहीं है, यह विधि के स्टैक पते के सापेक्ष ऑफसेट के समान कुछ हो सकता है। इसलिए, मैं किसी अन्य फ़ंक्शन (गैर-स्विस वाले) की तर्क सूची के साथ एक फ़ंक्शन (swizzled फ़ंक्शन) के objc_msgsend को कॉल नहीं कर सकता।
मैंने दृष्टिकोण बदलने और एनएसआईएनवोकेशन में सभी तर्कों की प्रतिलिपि बनाने की कोशिश की, लेकिन मुझे आमंत्रण के वापसी मूल्य को प्रबंधित करने में अन्य समस्याएं थीं, और यहां तक कि एक-एक करके तर्कों की प्रतिलिपि बनाना (सभी अलग-अलग प्रकारों के प्रबंधन) के लिए बहुत सारे कोड की आवश्यकता होती है, इसलिए मैं इस दृष्टिकोण पर लौटना पसंद करता हूं, जो मुझे क्लीनर लगता है (imho)
क्या आपके पास कोई सुझाव है? धन्यवाद
दोनों धन्यवाद, दोनों उत्तर बहुत उपयोगी हैं, मैं इसे एक सही के रूप में चिह्नित करूंगा क्योंकि एक स्पष्टीकरण प्लस एक दिलचस्प उदाहरण है। इन उत्तरों के कारण, मैंने एनएसआईएनवोकेशन के साथ पुनः प्रयास करने का फैसला किया, मैं इसके बारे में एक और सवाल पोस्ट करूंगा। धन्यवाद फिर से – LombaX
यदि कोई दिलचस्पी लेता है, तो यहां गिथब पर कक्षा के पहले संस्करण के लिए एक लिंक है: https://github.com/lombax85/FLCodeInjector – LombaX