स्थैतिक पुस्तकालय में उद्देश्य-सी श्रेणियां


153

क्या आप मुझे यह बता सकते हैं कि स्थिर पुस्तकालय को iPhone परियोजना से कैसे जोड़ा जाए। मैं प्रत्यक्ष निर्भरता (लक्ष्य -> ​​सामान्य -> ​​प्रत्यक्ष निर्भरता) के रूप में ऐप परियोजना में जोड़े गए स्थिर पुस्तकालय परियोजना का उपयोग करता हूं और सभी ठीक है, लेकिन श्रेणियां। स्टैटिक लाइब्रेरी में परिभाषित एक श्रेणी ऐप में काम नहीं कर रही है।

तो मेरा सवाल है कि कुछ श्रेणियों के साथ स्टेटिक लाइब्रेरी को अन्य प्रोजेक्ट में कैसे जोड़ा जाए?

और सामान्य तौर पर, अन्य परियोजनाओं से ऐप प्रोजेक्ट कोड में उपयोग करने के लिए सबसे अच्छा अभ्यास क्या है?


1
ठीक है, कुछ जवाब मिले और लगता है कि यह सवाल पहले से ही यहाँ उत्तर दिया गया था (क्षमा करें यह stackoverflow.com/questions/932856/… )
व्लादिमीर

जवाबों:


228

समाधान: Xcode 4.2 के रूप में, आपको केवल उस एप्लिकेशन पर जाना होगा जो लाइब्रेरी के खिलाफ लिंक कर रहा है (लाइब्रेरी ही नहीं) और प्रोजेक्ट नेविगेटर में प्रोजेक्ट पर क्लिक करें, अपने ऐप के लक्ष्य पर क्लिक करें, फिर सेटिंग बनाएं, फिर "अन्य खोजें" Linker Flags ", + बटन पर क्लिक करें, और '-ObjC' जोड़ें। '-all_load' और '-force_load' की अब आवश्यकता नहीं है।

विवरण: मुझे विभिन्न मंचों, ब्लॉग और ऐप्पल डॉक्स पर कुछ जवाब मिले। अब मैं अपनी खोजों और प्रयोगों का संक्षिप्त सारांश बनाने की कोशिश करता हूं।

समस्‍या के कारण (सेब तकनीकी Q & A QA1490 https://developer.apple.com/library/content/qa/qa1490/_index.html से उद्धरण ) हुआ था:

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

और उनका समाधान:

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

और iPhone विकास अक्सर पूछे जाने वाले प्रश्न में सिफारिश भी है:

मैं स्थैतिक पुस्तकालय में सभी उद्देश्य-सी कक्षाएं कैसे लिंक कर सकता हूं? अन्य लिंकर झंडे सेट -ObjC के लिए सेट करें।

और झंडे का विवरण:

- all_load स्थिर संग्रह लाइब्रेरीज़ के सभी सदस्यों को लोड करता है।

- ObjC सभी स्टैबर्ड आर्काइव लाइब्रेरीज़ के सदस्यों को लोड करता है जो ऑब्जेक्टिव-सी क्लास या कैटेगरी को लागू करते हैं।

- force_load (path_to_archive) निर्दिष्ट स्थिर संग्रह लाइब्रेरी के सभी सदस्यों को लोड करता है। नोट: -all_load सभी अभिलेखागार के सभी सदस्यों को लोड करने के लिए बाध्य करता है। यह विकल्प आपको एक विशिष्ट संग्रह को लक्षित करने की अनुमति देता है।

* हम बाइनरी आकार को कम करने के लिए और कुछ मामलों में All_load का कारण बन सकने वाले संघर्षों से बचने के लिए force_load का उपयोग कर सकते हैं।

हां, यह प्रोजेक्ट में * .a फ़ाइलों के साथ काम करता है। फिर भी मुझे सीधे निर्भरता के रूप में जोड़े गए वित्तीय परियोजना से परेशानी थी। लेकिन बाद में मैंने पाया कि यह मेरी गलती थी - प्रत्यक्ष निर्भरता परियोजना को संभवतः ठीक से नहीं जोड़ा गया था। जब मैं इसे हटा देता हूं और चरणों के साथ फिर से जोड़ता हूं:

  1. एप्लिकेशन प्रोजेक्ट में लीब प्रोजेक्ट फ़ाइल खींचें और छोड़ें (या प्रोजेक्ट के साथ जोड़ें-> प्रोजेक्ट में जोड़ें ...)।
  2. Lib प्रोजेक्ट आइकन पर तीर पर क्लिक करें - mylib.a फ़ाइल नाम दिखाया गया है, इस mylib.a फ़ाइल को खींचें और इसे टारगेट में ड्रॉप करें -> लिंक बाइनरी लाइब्रेरी समूह के साथ।
  3. मुट्ठी पृष्ठ (सामान्य) में लक्ष्य जानकारी खोलें और निर्भरता सूची में मेरा काम जोड़ें

उसके बाद सब ठीक है। "-ओबीजेसी" झंडा मेरे मामले में पर्याप्त था।

मैं भी http://iphonedevelopmentexperience.blogspot.com/2010/03/categories-in-static-library.html ब्लॉग से विचार के साथ रुचि रखता था । लेखक का कहना है कि वह सेटिंग -all_load या -ObjC ध्वज के बिना lib से श्रेणी का उपयोग कर सकता है। वह सिर्फ श्रेणी h / m फाइलें खाली डमी क्लास इंटरफ़ेस / कार्यान्वयन से जोड़ता है ताकि लिंकर को इस फाइल का उपयोग करने के लिए मजबूर किया जा सके। और हाँ, यह ट्रिक काम करती है।

लेकिन लेखक ने यह भी कहा कि उन्होंने डमी ऑब्जेक्ट को तुरंत नहीं दिया। Mm ... जैसा कि मैंने पाया है कि हमें स्पष्ट रूप से श्रेणी फ़ाइल से कुछ "वास्तविक" कोड को कॉल करना चाहिए। तो कम से कम वर्ग समारोह को बुलाया जाना चाहिए। और हमें डमी क्लास की भी जरूरत नहीं है। सिंगल सी फंक्शन भी यही करते हैं।

इसलिए यदि हम लिबास फाइल को इस प्रकार लिखते हैं:

// mylib.h
void useMyLib();

@interface NSObject (Logger)
-(void)logSelf;
@end


// mylib.m
void useMyLib(){
    NSLog(@"do nothing, just for make mylib linked");
}


@implementation NSObject (Logger)
-(void)logSelf{
    NSLog(@"self is:%@", [self description]);
}
@end

और अगर हम useMyLib () कहते हैं; App प्रोजेक्ट में कहीं भी किसी भी वर्ग में हम logSelf श्रेणी विधि का उपयोग कर सकते हैं;

[self logSelf];

और विषय पर अधिक ब्लॉग:

http://t-machine.org/index.php/2009/10/13/how-to-make-an-iphone-static-library-part-1/

http://blog.costan.us/2009/12/fat-iphone-static-libraries-device-and.html


8
Apple टेक नोट के बाद से यह कहने के लिए संशोधित किया गया है कि "इस समस्या को हल करने के लिए, स्थैतिक पुस्तकालय के खिलाफ लिंक को -ObjC लिंक करने वाले को पास करना होगा।" जो कि ऊपर उद्धृत के विपरीत है। हमने सिर्फ यह पुष्टि की है कि आपको ऐप को लिंक करते समय शामिल करना है, न कि लाइब्रेरी से।
केन एस्पेसलघ

Doc developer.apple.com/library/mac/#qa/qa1490/_index.html के अनुसार , हमें -all_load या -force_load ध्वज का उपयोग करना चाहिए। जैसा कि उल्लेख किया गया है, लिंकर के पास 64 बिट मैक ऐप और आईफोन ऐप में बग है। "महत्वपूर्ण: 64-बिट और iPhone OS अनुप्रयोगों के लिए, एक लिंकर बग होता है जो -ObjC को स्थिर लाइब्रेरी से ऑब्जेक्ट फ़ाइलों को लोड करने से रोकता है जिसमें केवल श्रेणियां और कोई वर्ग नहीं होते हैं। वर्कअराउंड -all_load या -force_D ध्वज का उपयोग करना है।"
रॉबिन

2
@ केन एस्पेलघ: धन्यवाद, मेरा भी यही मुद्दा था। -ObjC और -all_load झंडे को ऐप में ही जोड़ने की जरूरत है , लाइब्रेरी की नहीं।
टाइटेनियम

3
शानदार जवाब, हालांकि इस सवाल पर नए लोगों को ध्यान देना चाहिए कि यह अब पुराना है। Tonklon के जवाब की जाँच करें stackoverflow.com/a/9224606/322748 (all_load / force_load अब जरूरत है)
जे Peyer

मैं लगभग आधे घंटे के लिए इन चीजों पर अटक गया और एक परीक्षण और त्रुटि के साथ मैंने इसे बाहर कर दिया। कोई बात नहीं धन्यवाद। एक +1 के लायक यह जवाब और आपको मिल गया !!!
दीपुकजायन

118

व्लादिमीर से जवाब वास्तव में बहुत अच्छा है, हालांकि, मैं यहां कुछ और पृष्ठभूमि ज्ञान देना चाहूंगा। हो सकता है कि एक दिन किसी को मेरा जवाब मिल जाए और उसे मदद मिल जाए।

कंपाइलर स्रोत फ़ाइलों (.c, .cc, .cpp, .m) को ऑब्जेक्ट फ़ाइलों (.o) में बदल देता है। स्रोत फ़ाइल प्रति एक ऑब्जेक्ट फ़ाइल है। ऑब्जेक्ट फ़ाइलों में प्रतीक, कोड और डेटा होते हैं। ऑब्जेक्ट फ़ाइलें ऑपरेटिंग सिस्टम द्वारा सीधे उपयोग करने योग्य नहीं हैं।

अब जब एक गतिशील पुस्तकालय (.dylib), एक फ्रेमवर्क, एक लोड करने योग्य बंडल (.bundle) या एक निष्पादन योग्य बाइनरी का निर्माण किया जाता है, तो इन ऑब्जेक्ट फ़ाइलों को लिंकर द्वारा एक साथ जोड़ा जाता है ताकि कुछ उत्पादन करने के लिए ऑपरेटिंग सिस्टम "उपयोग करने योग्य" समझे, जैसे कुछ यह कर सकता है। सीधे एक विशिष्ट मेमोरी पते पर लोड होता है।

हालाँकि, एक स्थैतिक पुस्तकालय का निर्माण करते समय, इन सभी ऑब्जेक्ट फ़ाइलों को केवल एक बड़ी संग्रह फ़ाइल में जोड़ा जाता है, इसलिए स्थैतिक पुस्तकालयों (संग्रह के लिए एक .a) का विस्तार होता है। तो एक .a फ़ाइल ऑब्जेक्ट (.o) फ़ाइलों के संग्रह से कुछ भी नहीं है। संपीड़न के बिना एक TAR संग्रह या एक ज़िप संग्रह के बारे में सोचें। यह .o फ़ाइलों की एक पूरी गुच्छा (जावा के समान, जहाँ आप सेट करते हैं, के आसपास एक .a फ़ाइल को कॉपी करना आसान होता है। फ़ाइलों को आसान वितरण के लिए एक .jar संग्रह में विभाजित करें)।

किसी बाइनरी को स्टैटिक लाइब्रेरी (= आर्काइव) से लिंक करते समय, लिंकर को आर्काइव में सभी प्रतीकों की एक तालिका मिल जाएगी और जाँच करें कि इन प्रतीकों में से कौन से बायनेरिज़ द्वारा संदर्भित हैं। केवल संदर्भित फ़ाइलों वाले ऑब्जेक्ट ऑब्जेक्ट्स को वास्तव में लिंकर द्वारा लोड किया जाता है और लिंकिंग प्रक्रिया द्वारा माना जाता है। उदाहरण के लिए, यदि आपके संग्रह में 50 ऑब्जेक्ट फ़ाइलें हैं, लेकिन केवल 20 में बाइनरी द्वारा उपयोग किए गए प्रतीक हैं, केवल 20 को लिंकर द्वारा लोड किया गया है, अन्य 30 को लिंकिंग प्रक्रिया में पूरी तरह से अनदेखा किया गया है।

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

यदि लिंकर ओब्ज-सी कोड वाली ऑब्जेक्ट फ़ाइल को लोड करता है, तो उसके सभी ओबज-सी हिस्से हमेशा लिंकिंग स्टेज का हिस्सा होते हैं। इसलिए यदि श्रेणियों वाली कोई ऑब्जेक्ट फ़ाइल लोड की गई है, क्योंकि उसमें से किसी भी प्रतीक को "उपयोग में" माना जाता है (जैसा कि यह एक वर्ग है, यह एक फ़ंक्शन है, तो यह एक वैश्विक चर है), श्रेणियों को भी लोड किया जाता है और रनटाइम पर उपलब्ध होगा । फिर भी यदि ऑब्जेक्ट फ़ाइल स्वयं लोड नहीं है, तो उसमें श्रेणियां रनटाइम पर उपलब्ध नहीं होंगी। केवल श्रेणियों वाली एक ऑब्जेक्ट फ़ाइल कभी लोड नहीं होती है क्योंकि इसमें कोई प्रतीक नहीं होता है जो लिंकर कभी भी "उपयोग में" नहीं मानता है। और यह यहाँ पूरी समस्या है।

कई समाधान प्रस्तावित किए गए हैं और अब आप जानते हैं कि यह सब एक साथ कैसे चलता है, आइए प्रस्तावित समाधान पर एक और नज़र डालते हैं:

  1. एक समाधान -all_loadलिंकर कॉल में जोड़ना है। वह लिंकर ध्वज वास्तव में क्या करेगा? वास्तव में यह लिंकर को बताता है कि " सभी अभिलेखागार की सभी ऑब्जेक्ट फ़ाइलों को लोड करें, भले ही आपको उपयोग में कोई प्रतीक दिखाई दे या नहीं '। बेशक, यह काम करेगा; लेकिन यह बड़े बायनेरिज़ का उत्पादन भी कर सकता है।

  2. एक अन्य समाधान यह है -force_loadकि लिंकर कॉल को आर्काइव के पथ सहित जोड़ा जाए। यह ध्वज ठीक वैसे -all_loadही काम करता है , लेकिन केवल निर्दिष्ट संग्रह के लिए। बेशक यह भी काम करेगा।

  3. सबसे लोकप्रिय समाधान -ObjCलिंकर कॉल में जोड़ना है। वह लिंकर ध्वज वास्तव में क्या करेगा? यह ध्वज लिंकर को बताता है " सभी अभिलेखागार से सभी ऑब्जेक्ट फ़ाइलों को लोड करें यदि आप देखते हैं कि उनमें कोई ओब्ज-सी कोड है "। और "किसी भी ओब्ज-सी कोड" में श्रेणियां शामिल हैं। यह भी काम करेगा और यह बिना ओबज-सी कोड वाली ऑब्जेक्ट फ़ाइलों को लोड करने के लिए बाध्य नहीं करेगा (ये अभी भी केवल मांग पर लोड हैं)।

  4. एक और समाधान बल्कि नया Xcode बिल्ड सेटिंग है Perform Single-Object Prelink। यह सेटिंग क्या करेगी? यदि सक्षम किया गया है, तो सभी ऑब्जेक्ट फ़ाइलें (याद रखें, एक स्रोत फ़ाइल प्रति है) को एक ही ऑब्जेक्ट फ़ाइल में मिला दिया जाता है (जो कि वास्तविक लिंकिंग नहीं है, इसलिए नाम PreLink ) और यह एकल ऑब्जेक्ट फ़ाइल (कभी-कभी इसे "मास्टर ऑब्जेक्ट भी कहा जाता है") फ़ाइल ") तब संग्रह में जोड़ दी जाती है। यदि अब मास्टर ऑब्जेक्ट फ़ाइल के किसी भी प्रतीक को उपयोग में माना जाता है, तो पूरी मास्टर ऑब्जेक्ट फ़ाइल को उपयोग में माना जाता है और इस प्रकार सभी ऑब्जेक्टिव-सी भागों को हमेशा लोड किया जाता है। और चूंकि कक्षाएं सामान्य प्रतीक हैं, इसलिए ऐसी स्थिर लाइब्रेरी से एकल वर्ग का उपयोग करना भी सभी श्रेणियों को प्राप्त करना है।

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

बस आज के लिए इतना ही।

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

हालाँकि, यदि आप लिंकर को "डेड स्ट्रिप" बताते हैं, तो लिंकर सबसे पहले सभी ऑब्जेक्ट फ़ाइलों को बाइनरी में जोड़ देगा, सभी संदर्भों को हल कर देगा और अंत में बाइनरी को स्कैन करेगा प्रतीकों के लिए नहीं (या केवल अन्य प्रतीकों द्वारा उपयोग में नहीं है उपयोग)। तब उपयोग में नहीं आने वाले सभी प्रतीकों को अनुकूलन चरण के हिस्से के रूप में हटा दिया जाता है। ऊपर के उदाहरण में, 99 अप्रयुक्त कार्यों को फिर से हटा दिया जाता है। यह बहुत उपयोगी है यदि आप विकल्पों का उपयोग करते हैं -load_all, -force_loadया Perform Single-Object Prelinkक्योंकि ये विकल्प कुछ मामलों में नाटकीय रूप से बाइनरी आकार को आसानी से उड़ा सकते हैं और मृत स्ट्रिपिंग अप्रयुक्त कोड और डेटा को फिर से हटा देगा।

सी कोड के लिए डेड स्ट्रिपिंग बहुत अच्छी तरह से काम करता है (जैसे अप्रयुक्त कार्यों, चर और स्थिरांक को उम्मीद के अनुसार हटा दिया जाता है) और यह सी ++ के लिए भी काफी अच्छा काम करता है (जैसे अप्रयुक्त कक्षाएं हटा दी जाती हैं)। यह सही नहीं है, कुछ मामलों में कुछ प्रतीकों को हटाया नहीं जाता है भले ही उन्हें हटाना ठीक होगा, लेकिन ज्यादातर मामलों में यह इन भाषाओं के लिए काफी अच्छा काम करता है।

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

MyCoolClass * mcc = [[MyCoolClass alloc] init];

मैं भी लिख सकता था

NSString * cname = @"CoolClass";
NSString * cnameFull = [NSString stringWithFormat:@"My%@", cname];
Class mmcClass = NSClassFromString(cnameFull);
id mmc = [[mmcClass alloc] init];

दोनों मामलों mmcमें वर्ग "MyCoolClass" की एक वस्तु का संदर्भ है, लेकिन दूसरे कोड नमूने में इस वर्ग का कोई सीधा संदर्भ नहीं है (एक स्थिर स्ट्रिंग के रूप में वर्ग का नाम भी नहीं)। सब कुछ रनटाइम पर ही होता है। और है कि भले ही वर्गों है कर रहे हैं वास्तव में असली प्रतीकों। यह श्रेणियों के लिए और भी बुरा है, क्योंकि वे वास्तविक प्रतीक भी नहीं हैं।

इसलिए यदि आपके पास सैकड़ों वस्तुओं के साथ एक स्थिर पुस्तकालय है, फिर भी आपके अधिकांश बायनेरिज़ को उनमें से कुछ की आवश्यकता है, तो आप ऊपर दिए गए समाधानों (1) से (4) का उपयोग नहीं करना पसंद कर सकते हैं। अन्यथा आप इन सभी वर्गों वाले बहुत बड़े बायनेरिज़ के साथ समाप्त होते हैं, भले ही उनमें से अधिकांश का उपयोग कभी नहीं किया जाता है। कक्षाओं के लिए आपको आमतौर पर किसी विशेष समाधान की आवश्यकता नहीं होती है क्योंकि कक्षाओं में वास्तविक प्रतीक होते हैं और जब तक आप उन्हें सीधे (दूसरे कोड नमूने में नहीं) संदर्भित करते हैं, तब तक लिंकर अपने उपयोग की अच्छी तरह से पहचान कर लेगा। श्रेणियों के लिए, हालांकि, समाधान (5) पर विचार करें, क्योंकि यह केवल उन श्रेणियों को शामिल करना संभव बनाता है जिनकी आपको वास्तव में आवश्यकता है।

उदाहरण के लिए, यदि आप NSData के लिए एक श्रेणी चाहते हैं, उदाहरण के लिए, इसमें एक संपीड़न / अपघटन विधि जोड़कर, आप एक हेडर फ़ाइल बनाएंगे:

// NSData+Compress.h
@interface NSData (Compression)
    - (NSData *)compressedData;
    - (NSData *)decompressedData;
@end

void import_NSData_Compression ( );

और एक कार्यान्वयन फ़ाइल

// NSData+Compress
@implementation NSData (Compression)
    - (NSData *)compressedData 
    {
        // ... magic ...
    }

    - (NSData *)decompressedData
    {
        // ... magic ...
    }
@end

void import_NSData_Compression ( ) { }

अब बस यह सुनिश्चित कर लें कि आपके कोड में कहीं भी import_NSData_Compression()कॉल किया गया है। इससे कोई फर्क नहीं पड़ता कि इसे कहाँ कहा जाता है या इसे कितनी बार कहा जाता है। वास्तव में यह वास्तव में बिल्कुल नहीं बुलाया जा सकता है, यह काफी है अगर लिंकर ऐसा सोचता है। उदाहरण के लिए आप अपनी परियोजना में कहीं भी निम्नलिखित कोड डाल सकते हैं:

__attribute__((used)) static void importCategories ()
{
    import_NSData_Compression();
    // add more import calls here
}

आपको कभी भी importCategories()अपने कोड में कॉल करने की आवश्यकता नहीं है , विशेषता कंपाइलर और लिंकर को विश्वास दिलाएगा कि इसे कहा जाता है, भले ही ऐसा न हो।

और एक अंतिम टिप:
यदि आप -whyloadअंतिम लिंक कॉल में जोड़ते हैं, तो लिंकर बिल्ड लॉग में प्रिंट करेगा कि किस लाइब्रेरी से किस ऑब्जेक्ट ने यह लोड किया है क्योंकि उपयोग में कौन सा प्रतीक है। यह केवल उपयोग में माना जाने वाला पहला प्रतीक मुद्रित करेगा, लेकिन यह आवश्यक रूप से उस ऑब्जेक्ट फ़ाइल के उपयोग में एकमात्र प्रतीक नहीं है।


1
उल्लेख करने के लिए धन्यवाद -whyload, डिबग करने की कोशिश क्यों लिंकर कुछ कर रहा है काफी मुश्किल हो सकता है!
बेन एस

में एक विकल्प Dead Code Strippingहै Build Settings>Linking। क्या इसे उसी तरह से -dead_stripजोड़ा गया है Other Linker Flags?
जिओ

1
@ सीन हाँ, यह वही है। बस "त्वरित सहायता" पढ़ें जो हर बिल्ड सेटिंग के लिए मौजूद है, इसका उत्तर वहीं है: postimg.org/image/n7megftnr/full
Mecki

@ मेकी धन्यवाद। मैंने छुटकारा पाने की कोशिश की -ObjC, इसलिए मैंने आपके हैक की कोशिश की लेकिन यह शिकायत करता है "import_NSString_jsonObject()", referenced from: importCategories() in main.o ld: symbol(s) not found। मैंने import_NSString_jsonObjectअपने एम्बेडेड फ्रेमवर्क को नाम दिया है Utility, और मेरे अंत में बयान के #import <Utility/Utility.h>साथ जोड़ें । __attribute__AppDelegate.h
जिओ

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

24

यह मुद्दा एलएलवीएम में तय किया गया है । एलएलवीएम 2.9 के हिस्से के रूप में फिक्स जहाज। फिक्स को शामिल करने वाला पहला Xcode संस्करण LLVM 3.0 के साथ Xcode 4.2 शिपिंग है। XCode 4.2 के साथ काम करते समय या उसके उपयोग की अब आवश्यकता नहीं है-all_load-force_load -ObjC


क्या तुम इसके बारे में निश्चित हो? मैं एक iOS परियोजना पर काम कर रहा हूँ Xcode 4.3.2 का उपयोग, LLVM 3.1 के साथ संकलन और यह अभी भी मेरे लिए एक मुद्दा था।
एशले मिल्स

ठीक है, वह थोड़ा अड़चन था। -ObjCझंडा अभी भी जरूरत है और हमेशा रहेंगे। वर्कअराउंड का उपयोग -all_loadया था -force_load। और इसकी अब जरूरत नहीं है। मैंने अपना जवाब ऊपर दिया।
टोनक्लबोन

क्या -all_load ध्वज (भले ही यह अनावश्यक हो) को शामिल करने का कोई नुकसान है? क्या यह किसी भी तरह से संकलन / लॉन्च समय को प्रभावित करता है?
ZS

मैं Xcode वर्जन 4.5 (4G182) के साथ काम कर रहा हूं और -ObjC फ्लैग ने 3 पार्टी पर निर्भरता से मेरे पहचाने हुए चयनकर्ता की त्रुटि को हल कर दिया है। :]: उदाहरण के लिए न पहचाना गया चयनकर्ता..."। कोई सुराग?
राबर्ट एटकिंस

16

अपनी स्थैतिक लाइब्रेरी को संकलित करते समय इस समस्या को पूरी तरह हल करने के लिए आपको क्या करने की आवश्यकता है:

या तो Xcode बिल्ड सेटिंग्स में जाएं और Perform Single-Object Prelink को YES में या GENERATE_MASTER_OBJECT_FILE = YESअपनी बिल्ड कॉन्फ़िगरेशन फ़ाइल में सेट करें ।

डिफ़ॉल्ट रूप से, लिंकर प्रत्येक .m फ़ाइल के लिए एक .o फ़ाइल बनाता है। इसलिए श्रेणियों को अलग .o फाइलें मिलती हैं। जब लिंकर एक स्थिर पुस्तकालय .o फ़ाइलों को देखता है, तो यह प्रति वर्ग (रनटाइम होगा, अब कोई फर्क नहीं पड़ता) सभी प्रतीकों का एक सूचकांक नहीं बनाता है।

यह निर्देश लिंकर को सभी वस्तुओं को एक बड़ी .o फ़ाइल में एक साथ पैक करने के लिए कहेगा और इसके द्वारा वह लिंकर को बाध्य करता है जो सभी पुस्तकालय श्रेणियों को सूचकांक प्राप्त करने के लिए स्थैतिक पुस्तकालय की प्रक्रिया करता है।

आशा है कि यह स्पष्ट करता है।


इसने मुझे जोड़ने के लक्ष्य के लिए -ObjC को जोड़े बिना इसे ठीक कर दिया।
मैथ्यू क्रैंशव

BlocksKit लाइब्रेरी के नवीनतम संस्करण को अपडेट करने के बाद , मुझे इस समस्या को ठीक करने के लिए इस सेटिंग का उपयोग करना पड़ा (मैं पहले से ही -OjC ध्वज का उपयोग कर रहा था लेकिन फिर भी समस्या को देख रहा था)।
राकमोह

1
वास्तव में आपका उत्तर बिल्कुल सही नहीं है। मैं "एक ही वर्ग की सभी श्रेणियों को एक .o फ़ाइल में एक साथ पैक करने के लिए लिंकर को नहीं पूछता", यह लिंकर को एक स्थिर लाइब्रेरी बनाने से पहले सभी ऑब्जेक्ट फ़ाइलों (.o) को एक एकल, बड़ी ऑब्जेक्ट फ़ाइल से लिंक करने के लिए कहता है। उन्हें यह। एक बार किसी भी प्रतीक को पुस्तकालय से संदर्भित किया जाता है, सभी प्रतीकों को लोड किया जाता है। हालाँकि, यह काम नहीं करेगा यदि कोई प्रतीक संदर्भित नहीं है (जैसे यदि यह काम नहीं करेगा यदि पुस्तकालय में केवल श्रेणियां हैं)।
मीकी

मुझे नहीं लगता कि यह काम करेगा यदि आप मौजूदा कक्षाओं में श्रेणियों को जोड़ते हैं, जैसे कि NSData।
बॉब व्हिटमैन

मुझे भी मौजूदा वर्गों में श्रेणियां जोड़ने में समस्या हो रही है। मेरा प्लगइन रन टाइम में उन्हें पहचान नहीं सकता है।
डेविड डनहम

9

जब भी स्थैतिक पुस्तकालय को जोड़ने की चर्चा सामने आती है, तो एक कारक यह तथ्य है कि आपको बिल्ड चरणों में स्वयं श्रेणियों को भी शामिल करना होगा-> फ़ाइलों की प्रतिलिपि बनाएँ और स्वयं स्थैतिक पुस्तकालय के स्रोतों का संकलन करें

Apple ने इस तथ्य को अपने हाल ही में iOS में स्टेटिक लाइब्रेरीज़ का उपयोग करते हुए प्रकाशित नहीं किया है।

मैंने एक पूरा दिन बिताया है -objC और -all_load आदि सभी प्रकार के बदलावों की कोशिश कर रहा है .. लेकिन इसमें से कुछ भी नहीं निकला .. इस सवाल ने उस मुद्दे को मेरे ध्यान में लाया। (मुझे गलत मत समझो .. आपको अभी भी -objC सामान करना है .. लेकिन यह सिर्फ उसी से अधिक है)।

एक और कार्रवाई जिसने हमेशा मेरी मदद की है वह यह है कि मैं हमेशा शामिल किए गए स्थिर पुस्तकालय का निर्माण पहले अपने दम पर करता हूं .. फिर मैं संलग्न आवेदन का निर्माण करता हूं।


-1

आपको संभवतः स्थिर लाइब्रेरी के "सार्वजनिक" शीर्ष लेख में श्रेणी की आवश्यकता है: #import "MyStaticLib.h"

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