NSObject + load और + initialize - वे क्या करते हैं?


115

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

मेरी जिज्ञासा Apple के उदाहरण कोड - MVCNetworking को देखने से आती है। उनके मॉडल वर्ग में एक +(void) applicationStartupविधि है। यह फाइलसिस्टम पर कुछ हाउसकीपिंग करता है, NSDefaults, etc आदि पढ़ता है ... और, NSObject के क्लास के तरीकों को टटोलने के बाद, ऐसा लगता है कि यह चौकीदार काम + भार में डालना ठीक हो सकता है।

मैंने MVCNetworking प्रोजेक्ट को संशोधित किया, App Delegate to + applicationStartup में कॉल को हटा दिया, और हाउसकीपिंग बिट्स को + लोड में डाल दिया ... मेरे कंप्यूटर में आग नहीं लगी, लेकिन इसका मतलब यह नहीं है कि यह सही है! मैं एक कस्टम सेटअप विधि के आसपास किसी भी सूक्ष्मता, गोच और व्हॉट्स की समझ हासिल करने की उम्मीद कर रहा हूं, जिसे आपको बनाम + लोड या + इनिशियलाइज़ करना होगा।


+ लोड प्रलेखन के लिए कहते हैं:

लोड संदेश उन वर्गों और श्रेणियों को भेजा जाता है जो गतिशील रूप से लोड और सांख्यिकीय रूप से जुड़े होते हैं, लेकिन केवल तभी जब नया लोड किया गया वर्ग या श्रेणी एक ऐसी विधि लागू करती है जो प्रतिक्रिया दे सकती है।

यदि आप सभी शब्दों का सही अर्थ नहीं जानते हैं तो यह वाक्य कठिन और कठिन है। मदद!

  • "दोनों गतिशील रूप से लोड और सांख्यिकीय रूप से जुड़े हुए" से क्या मतलब है? क्या कुछ गतिशील रूप से लोड किया जा सकता है और सांख्यिकीय रूप से जुड़ा हुआ है, या वे पारस्परिक रूप से अनन्य हैं?

  • "... नई भरी हुई कक्षा या श्रेणी एक ऐसी विधि को लागू करती है जो प्रतिक्रिया दे सकती है" क्या विधि? कैसे जवाब दें?


+ आरंभिक रूप में, प्रलेखन कहता है:

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

मैं इसका मतलब यह है, "अगर आपके वर्ग की स्थापना की कोशिश कर रहा है ... प्रारंभ का उपयोग न करें।" अच्छा ठीक है। जब मैं शुरू में ओवरराइड करूंगा तो क्यों?

जवाबों:


184

loadसंदेश

रनटाइम loadप्रत्येक वर्ग ऑब्जेक्ट को संदेश भेजता है , बहुत जल्द ही क्लास ऑब्जेक्ट प्रक्रिया के पता स्थान में लोड होने के बाद। उन कक्षाओं के लिए जो प्रोग्राम के निष्पादन योग्य फ़ाइल का हिस्सा हैं, रनटाइम loadसंदेश को प्रक्रिया के जीवनकाल में बहुत पहले भेज देता है । एक साझा (डायनामिकली-लोडेड) लाइब्रेरी में मौजूद कक्षाओं के लिए, रनटाइम लोड संदेश को साझा लाइब्रेरी के लोड होने के तुरंत बाद प्रोसेस के एड्रेस स्पेस में भेज देता है।

इसके अलावा, रनटाइम केवल loadएक क्लास ऑब्जेक्ट को भेजता है यदि वह क्लास ऑब्जेक्ट खुद ही loadविधि लागू करता है। उदाहरण:

@interface Superclass : NSObject
@end

@interface Subclass : Superclass
@end

@implementation Superclass

+ (void)load {
    NSLog(@"in Superclass load");
}

@end

@implementation Subclass

// ... load not implemented in this class

@end

रनटाइम loadसंदेश को Superclassक्लास ऑब्जेक्ट पर भेजता है । यह वर्ग ऑब्जेक्ट को संदेश नहीं भेजता है , भले ही इससे विधि विरासत में मिली हो ।loadSubclassSubclassSuperclass

रनटाइम loadसंदेश को किसी क्लास ऑब्जेक्ट को तब भेजता है, जब उसने loadक्लास के सभी सुपरक्लास ऑब्जेक्ट्स (यदि वे सुपरक्लास ऑब्जेक्ट्स लागू होते हैं load) और आपके द्वारा लिंक किए गए साझा पुस्तकालयों में सभी क्लास ऑब्जेक्ट्स को मैसेज भेज दिया है। लेकिन आपको नहीं पता कि आपके अपने निष्पादन में कौन सी अन्य कक्षाएं loadअभी तक प्राप्त हुई हैं।

प्रत्येक कक्षा जिसे आपकी प्रक्रिया अपने पते के स्थान में लोड करती है load, उसे एक संदेश प्राप्त होगा , यदि वह loadविधि को लागू करता है, चाहे आपकी प्रक्रिया कक्षा का कोई अन्य उपयोग करती हो।

आप देख सकते हैं कि रनटाइम किस loadतरह से एक विशेष मामले के रूप में विधि को देखता _class_getLoadMethodहै objc-runtime-new.mm, और इसमें से सीधे कॉल करता call_class_loadsहै objc-loadmethod.mm

रनटाइम loadप्रत्येक श्रेणी की विधि को भी चलाता है , भले ही वह उसी श्रेणी के कई श्रेणियों को लागू करता हो load। यह असामान्य है। आम तौर पर, यदि दो श्रेणियां एक ही वर्ग पर समान विधि को परिभाषित करती हैं, तो विधियों में से एक "जीत" होगी और इसका उपयोग किया जाएगा, और दूसरी विधि कभी नहीं कहा जाएगा।

initializeविधि

रनटाइम क्लास वस्तु या कक्षा के किसी भी उदाहरण के लिए initializeपहला संदेश (अन्य से loadया initialize) भेजने से ठीक पहले एक क्लास ऑब्जेक्ट पर विधि को कॉल करता है । यह संदेश सामान्य तंत्र का उपयोग करके भेजा गया है, इसलिए यदि आपकी कक्षा लागू नहीं होती है initialize, लेकिन ऐसा वर्ग है, जो करता है, तो विरासत में मिला है, तो आपका वर्ग अपने सुपरक्लास का उपयोग करेगा initialize। रनटाइम initializeसभी को पहले वर्ग के सुपरक्लासेस में भेज देगा (यदि सुपरक्लास पहले से नहीं भेजा गया है initialize)।

उदाहरण:

@interface Superclass : NSObject
@end

@interface Subclass : Superclass
@end

@implementation Superclass

+ (void)initialize {
    NSLog(@"in Superclass initialize; self = %@", self);
}

@end

@implementation Subclass

// ... initialize not implemented in this class

@end

int main(int argc, char *argv[]) {
    @autoreleasepool {
        Subclass *object = [[Subclass alloc] init];
    }
    return 0;
}

यह कार्यक्रम आउटपुट की दो पंक्तियों को प्रिंट करता है:

2012-11-10 16:18:38.984 testApp[7498:c07] in Superclass initialize; self = Superclass
2012-11-10 16:18:38.987 testApp[7498:c07] in Superclass initialize; self = Subclass

चूंकि सिस्टम initializeविधि को आलसी तरीके से भेजता है , एक वर्ग को संदेश प्राप्त नहीं होगा जब तक कि आपका कार्यक्रम वास्तव में वर्ग (या उपवर्ग या वर्ग या उपवर्गों) को संदेश नहीं भेजता है। और जब तक आप प्राप्त करते हैं initialize, तब तक आपकी प्रक्रिया में प्रत्येक वर्ग को पहले से ही load(यदि उपयुक्त हो) प्राप्त होना चाहिए ।

इसे लागू करने का विहित तरीका initializeयह है:

@implementation Someclass

+ (void)initialize {
    if (self == [Someclass class]) {
        // do whatever
    }
}

इस पैटर्न का मतलब Someclassयह है कि जब यह उप-लागू नहीं होता है, तो इसे फिर से शुरू करने से बचना होगा initialize

रनटाइम फ़ंक्शन में initializeसंदेश भेजता _class_initializeहै objc-initialize.mm। आप देख सकते हैं कि यह इसे objc_msgSendभेजने के लिए उपयोग करता है, जो सामान्य संदेश भेजने वाला फ़ंक्शन है।

आगे की पढाई

की जाँच करें माइक ऐश शुक्रवार क्यू एंड ए इस विषय पर।


25
आपको ध्यान देना चाहिए कि +loadश्रेणियों के लिए अलग से भेजा गया है; अर्थात्, प्रत्येक वर्ग के प्रत्येक वर्ग में अपनी +loadपद्धति हो सकती है ।
जोनाथन ग्रियर्सन 22

1
यह भी ध्यान दें कि initializeएक loadविधि द्वारा सही ढंग से लागू किया जाएगा , यदि आवश्यक हो, loadतो असंगठित इकाई के संदर्भ के कारण । यह (अजीब तरह से, लेकिन समझदारी से) initializeपहले चलने के लिए नेतृत्व कर सकता है load! वैसे भी, मैंने जो मनाया है। यह "और जब तक आप प्राप्त करते हैं initialize, तब तक इसके विपरीत लगता है , आपकी प्रक्रिया में प्रत्येक वर्ग को पहले से ही load(यदि उपयुक्त हो) प्राप्त करना चाहिए था ।"
बेन्जोन

5
आप loadपहले प्राप्त करें । आप तब भी प्राप्त कर सकते हैं initializeजबकि loadअभी भी चल रहा है।
बजे लूट

1
@robmayoff हमें संबंधित विधियों के अंदर [सुपर इनिशियलाइज़] और [सुपर लोड] लाइनों को जोड़ने की आवश्यकता नहीं है?
दमिथा

1
यह आमतौर पर एक बुरा विचार है, क्योंकि रनटाइम ने उन सभी संदेशों को आपके सभी सुपरक्लास को पहले ही भेज दिया है, इससे पहले कि वह आपको भेजता है।
12 मई को

17

इसका मतलब यह है कि +initializeएक श्रेणी में ओवरराइड नहीं है , आप शायद कुछ तोड़ देंगे।

+loadउस वर्ग या श्रेणी के अनुसार एक बार बुलाया जाता है +load, जैसे ही उस वर्ग या श्रेणी को लोड किया जाता है। जब यह "स्टेटिकली लिंक्ड" कहता है तो इसका मतलब है कि आपके ऐप बाइनरी में संकलित है। इस +loadप्रकार संकलित की गई विधियों को आपके ऐप के लॉन्च होने पर निष्पादित किया जाएगा, संभवत: प्रवेश करने से पहले main()। जब यह "डायनामिकली लोडेड" कहता है, तो इसका अर्थ है कि प्लग इन बंडल या कॉल टू के माध्यम से लोड किया गया dlopen()। यदि आप iOS पर हैं, तो आप उस मामले को अनदेखा कर सकते हैं।

+initializeपहली बार उस संदेश को कक्षा में भेजे जाने से पहले कहा जाता है, उस संदेश को संभालने से पहले। यह (स्पष्ट रूप से) केवल एक बार होता है। यदि आप +initializeकिसी श्रेणी में ओवरराइड करते हैं , तो तीन चीजों में से एक होगा:

  • आपकी श्रेणी के कार्यान्वयन को बुलाया जाता है और कक्षा का कार्यान्वयन नहीं होता है
  • किसी और की श्रेणी के कार्यान्वयन को बुलाया जाता है; आपने कुछ नहीं लिखा
  • आपकी श्रेणी अभी तक लोड नहीं हुई है और इसके कार्यान्वयन को कभी भी कॉल नहीं किया जाता है।

यही कारण है कि आपको कभी भी +initializeकिसी श्रेणी में ओवरराइड नहीं करना चाहिए - वास्तव में किसी श्रेणी में किसी भी तरीके को आजमाना और बदलना बहुत खतरनाक है क्योंकि आप कभी भी निश्चित नहीं होते हैं कि आप क्या बदल रहे हैं या क्या आपका खुद का प्रतिस्थापन दूसरी श्रेणी द्वारा बंद हो जाएगा।

BTW, के साथ विचार करने के लिए एक और मुद्दा +initializeयह है कि अगर कोई आपको उपवर्गित करता है, तो आपको संभावित रूप से एक बार अपनी कक्षा के लिए और एक बार प्रत्येक वर्ग के लिए बुलाया जाएगा। यदि आप staticवैरिएबल सेट करने जैसा कुछ कर रहे हैं , तो आप उससे बचाव करना चाहेंगे: या तो dispatch_once()परीक्षण करके self == [MyClass class]

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.