Objective-C Language
निम्न-स्तरीय रनटाइम पर्यावरण
खोज…
टिप्पणियों
ऑब्जेक्टिव-सी रनटाइम का उपयोग करने के लिए, आपको इसे आयात करने की आवश्यकता है।
#import <objc/objc.h>
ऑब्जेक्ट को किसी अन्य मौजूदा ऑब्जेक्ट (एसोसिएशन) में संलग्न करें
किसी ऑब्जेक्ट को किसी मौजूदा ऑब्जेक्ट से जोड़ना संभव है जैसे कि कोई नई प्रॉपर्टी थी। इसे एसोसिएशन कहा जाता है और मौजूदा वस्तुओं का विस्तार करने की अनुमति देता है। इसका उपयोग वर्ग विस्तार के माध्यम से संपत्ति जोड़ते समय भंडारण प्रदान करने के लिए किया जा सकता है या अन्यथा किसी मौजूदा वस्तु में अतिरिक्त जानकारी जोड़ सकता है।
एक बार लक्ष्य ऑब्जेक्ट के हट जाने पर संबंधित ऑब्जेक्ट स्वचालित रूप से रनटाइम द्वारा जारी किया जाता है।
#import <objc/runtime.h>
// "Key" for association. Its value is never used and doesn't
// matter. The only purpose of this global static variable is to
// provide a guaranteed unique value at runtime: no two distinct
// global variables can share the same address.
static char key;
id target = ...;
id payload = ...;
objc_setAssociateObject(target, &key, payload, OBJC_ASSOCIATION_RETAIN);
// Other useful values are OBJC_ASSOCIATION_COPY
// and OBJ_ASSOCIATION_ASSIGN
id queryPayload = objc_getAssociatedObject(target, &key);
मेथड स्विज़लिंग का उपयोग करके विधियों को संवर्धित करना
ऑब्जेक्टिव-सी रनटाइम आपको रनटाइम पर एक विधि के कार्यान्वयन को बदलने की अनुमति देता है। इसे विधि स्विज़लिंग कहा जाता है और अक्सर दो तरीकों के कार्यान्वयन का आदान-प्रदान करने के लिए उपयोग किया जाता है। उदाहरण के लिए, अगर विधियों foo
और bar
का आदान-प्रदान किया जाता है, तो संदेश foo
भेजना अब bar
और इसके विपरीत के कार्यान्वयन को निष्पादित करेगा।
इस तकनीक का उपयोग मौजूदा तरीकों को बढ़ाने या "पैच" करने के लिए किया जा सकता है, जिसे आप सीधे संपादित नहीं कर सकते हैं, जैसे कि सिस्टम-प्रदान की गई कक्षाएं।
निम्नलिखित उदाहरण में, -[NSUserDefaults synchronize]
विधि मूल कार्यान्वयन के निष्पादन समय को प्रिंट करने के लिए संवर्धित है।
महत्वपूर्ण: बहुत से लोग का उपयोग कर swizzling करने की कोशिश method_exchangeImplementations
। हालाँकि, यह दृष्टिकोण खतरनाक है यदि आपको उस विधि को कॉल करने की आवश्यकता है जिसे आप प्रतिस्थापित कर रहे हैं, क्योंकि आप इसे प्राप्त करने की अपेक्षा कर रहे हैं की तुलना में एक अलग चयनकर्ता का उपयोग कर रहे हैं। परिणामस्वरूप, आपका कोड अजीब और अप्रत्याशित तरीके से टूट सकता है - खासकर अगर कई पार्टियां इस तरह से किसी वस्तु को स्वाइल करती हैं। इसके बजाय, आपको हमेशा C फ़ंक्शन के साथ संयोजन में setImplementation का उपयोग करके setImplementation
करना चाहिए, जिससे आप मूल चयनकर्ता के साथ विधि को कॉल कर सकते हैं।
#import "NSUserDefaults+Timing.h"
#import <objc/runtime.h> // Needed for method swizzling
static IMP old_synchronize = NULL;
static void new_synchronize(id self, SEL _cmd);
@implementation NSUserDefaults(Timing)
+ (void)load
{
Method originalMethod = class_getInstanceMethod([self class], @selector(synchronize:));
IMP swizzleImp = (IMP)new_synchronize;
old_synchronize = method_setImplementation(originalMethod, swizzleImp);
}
@end
static void new_synchronize(id self, SEL _cmd);
{
NSDate *started;
BOOL returnValue;
started = [NSDate date];
// Call the original implementation, passing the same parameters
// that this function was called with, including the selector.
returnValue = old_synchronize(self, _cmd);
NSLog(@"Writing user defaults took %f seconds.", [[NSDate date] timeIntervalSinceDate:started]);
return returnValue;
}
@end
यदि आपको एक ऐसी विधि अपनाने की आवश्यकता है जो पैरामीटर लेती है, तो आप उन्हें फ़ंक्शन में अतिरिक्त पैरामीटर के रूप में जोड़ते हैं। उदाहरण के लिए:
static IMP old_viewWillAppear_animated = NULL;
static void new_viewWillAppear_animated(id self, SEL _cmd, BOOL animated);
...
Method originalMethod = class_getClassMethod([UIViewController class], @selector(viewWillAppear:));
IMP swizzleImp = (IMP)new_viewWillAppear_animated;
old_viewWillAppear_animated = method_setImplementation(originalMethod, swizzleImp);
...
static void new_viewWillAppear_animated(id self, SEL _cmd, BOOL animated)
{
...
old_viewWillAppear_animated(self, _cmd, animated);
...
}
सीधे तौर पर कॉलिंग के तरीके
यदि आपको C कोड से एक Objective-C पद्धति को कॉल करने की आवश्यकता है, तो आपके पास दो तरीके हैं: objc_msgSend
का उपयोग objc_msgSend
, या IMP
(विधि कार्यान्वयन फ़ंक्शन पॉइंटर) प्राप्त करना और कॉल करना।
#import <objc/objc.h>
@implementation Example
- (double)negate:(double)value {
return -value;
}
- (double)invert:(double)value {
return 1 / value;
}
@end
// Calls the selector on the object. Expects the method to have one double argument and return a double.
double performSelectorWithMsgSend(id object, SEL selector, double value) {
// We declare pointer to function and cast `objc_msgSend` to expected signature.
// WARNING: This step is important! Otherwise you may get unexpected results!
double (*msgSend)(id, SEL, double) = (typeof(msgSend)) &objc_msgSend;
// The implicit arguments of self and _cmd need to be passed in addition to any explicit arguments.
return msgSend(object, selector, value);
}
// Does the same as the above function, but by obtaining the method's IMP.
double performSelectorWithIMP(id object, SEL selector, double value) {
// Get the method's implementation.
IMP imp = class_getMethodImplementation([self class], selector);
// Cast it so the types are known and ARC can work correctly.
double (*callableImp)(id, SEL, double) = (typeof(callableImp)) imp;
// Again, you need the explicit arguments.
return callableImp(object, selector, value);
}
int main() {
Example *e = [Example new];
// Invoke negation, result is -4
double x = performSelectorWithMsgSend(e, @selector(negate:), 4);
// Invoke inversion, result is 0.25
double y = performSelectorWithIMP(e, @selector(invert:), 4);
}
objc_msgSend
, विधि के लिए छोटा सा भूत प्राप्त करके और कॉल करके काम करता है। पिछले कई तरीकों के लिए IMP
कैश किया गया है, इसलिए यदि आप एक बहुत तंग पाश में एक उद्देश्य सी संदेश भेज रहे हैं तो आप स्वीकार्य प्रदर्शन प्राप्त कर सकते हैं। कुछ मामलों में, मैन्युअल रूप से कैचिंग छोटा सा भूत थोड़ा बेहतर प्रदर्शन दे सकता है, हालांकि यह एक अंतिम उपाय अनुकूलन है।