आप एक आइवर का उपयोग क्यों करेंगे?


153

मैं आमतौर पर इस प्रश्न को दूसरे तरीके से देखता हूं, जैसे कि हर हाथी को एक संपत्ति होना चाहिए? (और मुझे इस क्यू के बीबूम का जवाब पसंद है)।

मैं अपने कोड में लगभग विशेष रूप से गुणों का उपयोग करता हूं। हर बार, हालांकि, मैं एक ठेकेदार के साथ काम करता हूं जो लंबे समय से आईओएस पर विकसित हो रहा है और एक पारंपरिक गेम प्रोग्रामर है। वह कोड लिखता है जो लगभग कोई भी गुण नहीं बताता है और आइवर पर झूठ बोलता है। मुझे लगता है कि वह ऐसा इसलिए करता है क्योंकि 1.) वह इसका इस्तेमाल करता है क्योंकि संपत्तियों में हमेशा ऐसा नहीं होता था जब तक कि एक गटर / सेटर के माध्यम से नहीं जाने के न्यूनतम प्रदर्शन लाभ के लिए ऑब्जेक्टिव सी 2.0 (अक्टूबर '07) और 2.)।

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

मेरा सवाल और है ... आप एक आइवर अवधि का उपयोग क्यों करना चाहते हैं - अनुभवी या नहीं। क्या वास्तव में एक महान अंतर है कि एक आइवर का उपयोग करना उचित होगा?

स्पष्टीकरण के एक बिंदु के रूप में, मैं आवश्यकतानुसार बसने और पाने वालों को ओवरराइड करता हूं और उस संपत्ति का उपयोग करता हूं जो कि गेटर / सेटर के अंदर उस संपत्ति के साथ संबंध रखता है। हालाँकि, गेट्टर / सेटर या इनिट के बाहर, मैं हमेशा self.myPropertyसिंटैक्स का उपयोग करता हूं ।


संपादित करें 1

मैं सभी अच्छी प्रतिक्रियाओं की सराहना करता हूं। एक कि मुझे पता है कि गलत लगता है कि एक ivar के साथ आप एक संपत्ति है जहाँ आप नहीं के साथ encapsulation मिल रहा है करना चाहते हैं। बस एक वर्ग की निरंतरता में संपत्ति को परिभाषित करें। यह संपत्ति को बाहरी लोगों से छिपाएगा। आप संपत्ति को इंटरफ़ेस में आसानी से घोषित कर सकते हैं और इसे कार्यान्वयन में रीडराइट के रूप में पुन: परिभाषित कर सकते हैं:

// readonly for outsiders
@property (nonatomic, copy, readonly) NSString * name;

और कक्षा में जारी है:

// readwrite within this file
@property (nonatomic, copy) NSString * name;

इसे पूरी तरह से "निजी" करने के लिए केवल इसे कक्षा की निरंतरता में घोषित करें।


2
दिलचस्प सवाल के लिए upvote - अच्छी तरह से और एक भी है कि मैं ivars के लिए मामले को सुनना चाहूंगा क्योंकि ऐसा लगता है कि मुझे सैम का तरीका करना सिखाया गया है।
दमोह

2
ध्यान दें कि स्वचालित संदर्भ काउंटिंग (ARC) गुण के रूप में ivars के लिए समान स्मृति प्रबंधन लाभ लागू करता है, इसलिए ARC कोड में अंतर वास्तव में एनकैप्सुलेशन के बारे में है।
बेंज़ादो

1
आपका प्रश्न और विशेष रूप से संपादित 1 भाग वास्तव में बहुत अधिक जानकारीपूर्ण है तो चुना हुआ उत्तर।
user523234

1
Edit1: मुझे लगता है कि हर प्रॉपर्टी को पढ़ना और लिखना संभव है, यहां तक ​​कि जब केवल एक .h में पढ़ने की घोषणा की जाए, तो की-वैल्यू-कोडिंग, जैसे: [ऑब्जेक्ट सेटवैल्यू: [NSNumber numberWithInt] 20] forKey: @ "propertyname "];
Binarian

1
@ अपने संपादन 1 पर जाएँ: यदि आप एक निजी संपत्ति का उपयोग करते हैं और .m फ़ाइल में वर्ग एक्सटेंशन / निरंतरता का उपयोग करते हैं, तो यह उपवर्गों के लिए दृश्यमान नहीं है। आपको फिर से कोड लिखने या क्लास एक्सटेंशन के साथ एक और .h का उपयोग करने की आवश्यकता है। @ सुरक्षित / डिफ़ॉल्ट के साथ आसान।
बिनियन

जवाबों:


100

encapsulation

यदि आइवर निजी है, तो कार्यक्रम के अन्य भाग उस पर आसानी से नहीं मिल सकते हैं। घोषित संपत्ति के साथ, चतुर लोग एक्सेसर्स के माध्यम से आसानी से पहुंच सकते हैं और उत्परिवर्तित कर सकते हैं।

प्रदर्शन

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

Nontrivial प्रकार

उदाहरण: यदि आपके पास C ++ प्रकार है, तो सीधी पहुंच कभी-कभी बेहतर दृष्टिकोण है। प्रकार कॉपी करने योग्य नहीं हो सकता है, या यह कॉपी करने के लिए तुच्छ नहीं हो सकता है।

बहु सूत्रण

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

कार्यक्रम की शुद्धता

चूंकि उप-वर्ग किसी भी विधि को ओवरराइड कर सकते हैं, आप अंततः देख सकते हैं कि इंटरफ़ेस के लेखन में बनाम आपके राज्य को उचित रूप से प्रबंधित करने के बीच एक अर्थपूर्ण अंतर है। प्रोग्राम की शुद्धता के लिए सीधी पहुंच विशेष रूप से आंशिक रूप से निर्मित राज्यों में सामान्य है - आपके शुरुआती और dealloc, में सीधे पहुंच का उपयोग करना सबसे अच्छा है। तुम भी एक्सेसर, एक सुविधा की निर्माता, के कार्यान्वयन में इस आम मिल सकता है copy, mutableCopy, और संग्रह / क्रमबद्धता कार्यान्वयन।

यह भी अधिक बार होता है क्योंकि सब कुछ से एक चाल है एक सार्वजनिक readwrite accessor मानसिकता जो एक करने के लिए अपने कार्यान्वयन विवरण / डेटा अच्छी तरह से छुपाता है। कभी-कभी आपको साइड इफेक्ट के बारे में सही ढंग से कदम रखने की जरूरत होती है ताकि सही काम करने के लिए एक उपवर्ग ओवरराइड शुरू हो सके।

बाइनरी आकार

जब आप अपने प्रोग्राम के निष्पादन पर एक पल के लिए विचार करते हैं, तो डिफ़ॉल्ट रूप से रीडायरेक्ट की घोषणा करने से आमतौर पर कई एक्सेसर तरीकों की आवश्यकता होती है। तो यह आपके कार्यक्रम में कुछ वसा जोड़ देगा और साथ ही समय को लोड करेगा।

जटिलता को कम करता है

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


यह कहना बिल्कुल नहीं है कि गुणों या एक्सेसर्स का उपयोग करना बुरा है - प्रत्येक के महत्वपूर्ण लाभ और प्रतिबंध हैं। कई ओओ भाषाओं और डिजाइन के दृष्टिकोण के समान, आपको ओबीसी में उपयुक्त दृश्यता वाले एक्सेसर्स का भी पक्ष लेना चाहिए। कई बार आपको विचलन करने की आवश्यकता होगी। उस कारण से, मुझे लगता है कि कार्यान्वयन के लिए सीधी पहुंच को प्रतिबंधित करना सबसे अच्छा है जो कि ivar घोषित करता है (उदाहरण के लिए इसे घोषित करता है @private)।


1 संपादित करें:

हम में से अधिकांश ने याद रखा है कि गतिशील रूप से एक छिपे हुए एक्सेसर को कैसे कॉल किया जाए (जब तक हम नाम जानते हैं ...)। इस बीच, हम में से अधिकांश ने यह नहीं याद किया कि कैसे आइवीआर को ठीक से एक्सेस किया जाए जो कि दिखाई नहीं दे रहा है (केवीसी से परे)। वर्ग की निरंतरता मदद करती है , लेकिन यह कमजोरियों का परिचय देती है।

यह स्पष्ट है:

if ([obj respondsToSelector:(@selector(setName:)])
  [(id)obj setName:@"Al Paca"];

अब इसे केवल आईवीआर के साथ और केवीसी के बिना आज़माएं।


@ धन्यवाद, और अच्छा सवाल! पुन: जटिलता: यह निश्चित रूप से दोनों तरह से जाता है। फिर कैप्सूलीकरण - अद्यतन
जस्टिन

@bbum RE: विशिष्ट उदाहरण हालांकि मैं आपसे सहमत हूं कि यह गलत समाधान है, मैं कल्पना नहीं कर सकता कि कई अनुभवी objc देवों का मानना ​​है कि ऐसा नहीं होता है; मैंने इसे दूसरों के कार्यक्रमों में देखा है और ऐप स्टोर निजी ऐप्पल एपीआई के उपयोग पर प्रतिबंध लगाते हुए चले गए हैं।
जस्टिन

1
क्या आप किसी निजी आइवर को ऑब्जेक्ट-> फू से एक्सेस नहीं कर सकते? याद रखना उतना कठिन नहीं।
निक लॉकवुड

1
मेरा मतलब था कि आप सी -> सिंटैक्स का उपयोग करके ऑब्जेक्ट से एक पॉइंटर डिफरेंस का उपयोग करके इसे एक्सेस कर सकते हैं। ऑब्जेक्टिव-सी कक्षाएं मूल रूप से केवल हुड के नीचे की संरचनाएं होती हैं, और एक संरचना को एक संकेतक दिया जाता है, सदस्यों तक पहुंचने के लिए सी सिंटैक्स है ->, जो उद्देश्य सी कक्षाओं में भी ivars के लिए काम करता है।
निक लॉकवुड

1
@NickLockwood अगर ivar है @private, तो संकलक को कक्षा के बाहर के सदस्य की पहुंच और उदाहरण के तरीकों से मना करना चाहिए - क्या वह नहीं है जो आप देखते हैं?
जस्टिन

76

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

एक गेटर / सेटिंग के माध्यम से एक आइवर को एक्सेस करने में एक ऑब्जेक्टिव-सी मेथड कॉल शामिल है, जो "सामान्य" सी फ़ंक्शन कॉल की तुलना में बहुत कम (कम से कम 3-4 बार) है और यहां तक ​​कि एक सामान्य सी फ़ंक्शन कॉल पहले से कई गुना धीमी होगी। एक संरचनात्मक सदस्य तक पहुँचना। आपकी संपत्ति की विशेषताओं के आधार पर, संकलक द्वारा उत्पन्न सेटर / गेट्टर कार्यान्वयन में फ़ंक्शन objc_getProperty/ के लिए एक और C फ़ंक्शन कॉल शामिल हो सकता है objc_setProperty, क्योंकि इनमें आवश्यक वस्तुओं के रूप में retain/ copy/ autoreleaseऔर आगे परमाणु गुणों के लिए स्पिनलॉकिंग करना आवश्यक होगा। यह आसानी से बहुत महंगा हो सकता है और मैं 50% धीमा होने की बात नहीं कर रहा हूं।

चलो यह करके देखें:

CFAbsoluteTime cft;
unsigned const kRuns = 1000 * 1000 * 1000;

cft = CFAbsoluteTimeGetCurrent();
for (unsigned i = 0; i < kRuns; i++) {
    testIVar = i;
}
cft = CFAbsoluteTimeGetCurrent() - cft;
NSLog(@"1: %.1f picoseconds/run", (cft * 10000000000.0) / kRuns);

cft = CFAbsoluteTimeGetCurrent();
for (unsigned i = 0; i < kRuns; i++) {
    [self setTestIVar:i];
}
cft = CFAbsoluteTimeGetCurrent() - cft;
NSLog(@"2: %.1f picoseconds/run", (cft * 10000000000.0) / kRuns);

आउटपुट:

1: 23.0 picoseconds/run
2: 98.4 picoseconds/run

यह 4.28 गुना धीमा है और यह एक गैर-परमाणु आदिम int था, बहुत अच्छा मामला है ; अधिकांश अन्य मामले और भी बुरे हैं (परमाणु NSString *संपत्ति का प्रयास करें !)। तो अगर आप इस तथ्य के साथ रह सकते हैं कि प्रत्येक आइवर की पहुंच 4-5 गुना धीमी हो सकती है, तो गुणों का उपयोग करना ठीक है (कम से कम जब यह प्रदर्शन की बात आती है), हालांकि, ऐसी बहुत सी परिस्थितियां हैं जहां इस तरह का प्रदर्शन ड्रॉप है पूरी तरह से अस्वीकार्य।

अपडेट 2015-10-20

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

नीचे दिए गए कोड Accountवस्तुओं को परिभाषित करता है। एक खाते में ऐसे गुण होते हैं जो अपने मालिक के नाम ( NSString *), लिंग ( enum) और उम्र ( unsigned) के साथ-साथ एक संतुलन ( int64_t) का वर्णन करते हैं । एक खाता ऑब्जेक्ट में एक initविधि और एक compare:विधि है। compare:के रूप में परिभाषित किया गया है विधि: महिला आदेश पुरुष से पहले, नाम वर्णानुक्रम आदेश, वर्ष, संतुलन आदेश निम्न से उच्च से पहले युवा आदेश।

वास्तव में दो खाता कक्षाएं मौजूद हैं, AccountAऔर AccountB। यदि आप उनके कार्यान्वयन को देखते हैं, तो आप देखेंगे कि वे लगभग पूरी तरह समान हैं, एक अपवाद के साथ: compare:विधि। AccountAवस्तुएं अपने स्वयं के गुणों को विधि (गटर) द्वारा एक्सेस करती हैं , जबकि AccountBऑब्जेक्ट अपने गुणों को आइवर द्वारा एक्सेस करते हैं । यही वास्तव में एकमात्र अंतर है! वे दोनों दूसरे ऑब्जेक्ट के गुणों को गेट्टर से तुलना करने के लिए एक्सेस करते हैं (इसे आइवर द्वारा एक्सेस करना सुरक्षित नहीं होगा! क्या होगा यदि अन्य ऑब्जेक्ट एक उपवर्ग है और गेट्टर को ओवरराइड कर दिया है?)। यह भी ध्यान दें कि ivars के रूप में अपने स्वयं के गुणों तक पहुँचने से एनकैप्सुलेशन (ivars अभी भी सार्वजनिक नहीं हैं) नहीं टूटता है

परीक्षण सेटअप वास्तव में सरल है: 1 Mio यादृच्छिक खाते बनाएं, उन्हें एक सरणी में जोड़ें और उस सरणी को सॉर्ट करें। बस। बेशक, दो सरणियाँ हैं, एक AccountAवस्तुओं के लिए और एक AccountBवस्तुओं के लिए और दोनों सरणियाँ समान खातों (समान डेटा स्रोत) से भरी हुई हैं। हम समय को एरेज़ को सॉर्ट करने में कितना समय लेते हैं।

यहां मैंने कल किए गए कई रनों का उत्पादन किया है:

runTime 1: 4.827070, 5.002070, 5.014527, 5.019014, 5.123039
runTime 2: 3.835088, 3.804666, 3.792654, 3.796857, 3.871076

जैसा कि आप देख सकते हैं, AccountBवस्तुओं के सरणी को क्रमबद्ध करने की तुलना में वस्तुओं की सरणी को क्रमबद्ध करना हमेशा महत्वपूर्ण होता है AccountA

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

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

और यहां तक ​​कि अगर आप सीपीयू समय के संदर्भ में नहीं सोचते हैं, क्योंकि आपको लगता है कि सीपीयू समय बर्बाद करना पूरी तरह से स्वीकार्य है, आखिरकार "यह मुफ़्त है", तो बिजली की खपत के कारण सर्वर होस्टिंग लागत के बारे में क्या? मोबाइल उपकरणों की बैटरी रनटाइम के बारे में क्या? यदि आप एक ही मोबाइल ऐप को दो बार लिखते हैं (उदाहरण के लिए एक मोबाइल वेब ब्राउज़र), तो एक बार एक ऐसा संस्करण जहां सभी वर्ग अपने गुणों को केवल गेटर्स द्वारा एक्सेस करते हैं और एक बार जहां सभी वर्ग केवल आइवर द्वारा उन्हें एक्सेस करते हैं, पहले एक लगातार उपयोग करके निश्चित रूप से नाली होगी। बैटरी एक दूसरे का उपयोग करने की तुलना में बहुत तेज है, भले ही वे कार्यात्मक समतुल्य हैं और उपयोगकर्ता के लिए दूसरा एक भी शायद थोड़ा स्वैटर महसूस होगा।

अब यहाँ आपकी main.mफाइल के लिए कोड (ARC सक्षम होने पर कोड निर्भर करता है और पूर्ण प्रभाव देखने के लिए संकलन करते समय अनुकूलन का उपयोग करना सुनिश्चित करें):

#import <Foundation/Foundation.h>

typedef NS_ENUM(int, Gender) {
    GenderMale,
    GenderFemale
};


@interface AccountA : NSObject
    @property (nonatomic) unsigned age;
    @property (nonatomic) Gender gender;
    @property (nonatomic) int64_t balance;
    @property (nonatomic,nonnull,copy) NSString * name;

    - (NSComparisonResult)compare:(nonnull AccountA *const)account;

    - (nonnull instancetype)initWithName:(nonnull NSString *const)name
        age:(const unsigned)age gender:(const Gender)gender
        balance:(const int64_t)balance;
@end


@interface AccountB : NSObject
    @property (nonatomic) unsigned age;
    @property (nonatomic) Gender gender;
    @property (nonatomic) int64_t balance;
    @property (nonatomic,nonnull,copy) NSString * name;

    - (NSComparisonResult)compare:(nonnull AccountB *const)account;

    - (nonnull instancetype)initWithName:(nonnull NSString *const)name
        age:(const unsigned)age gender:(const Gender)gender
        balance:(const int64_t)balance;
@end


static
NSMutableArray * allAcocuntsA;

static
NSMutableArray * allAccountsB;

static
int64_t getRandom ( const uint64_t min, const uint64_t max ) {
    assert(min <= max);
    uint64_t rnd = arc4random(); // arc4random() returns a 32 bit value only
    rnd = (rnd << 32) | arc4random();
    rnd = rnd % ((max + 1) - min); // Trim it to range
    return (rnd + min); // Lift it up to min value
}

static
void createAccounts ( const NSUInteger ammount ) {
    NSArray *const maleNames = @[
        @"Noah", @"Liam", @"Mason", @"Jacob", @"William",
        @"Ethan", @"Michael", @"Alexander", @"James", @"Daniel"
    ];
    NSArray *const femaleNames = @[
        @"Emma", @"Olivia", @"Sophia", @"Isabella", @"Ava",
        @"Mia", @"Emily", @"Abigail", @"Madison", @"Charlotte"
    ];
    const NSUInteger nameCount = maleNames.count;
    assert(maleNames.count == femaleNames.count); // Better be safe than sorry

    allAcocuntsA = [NSMutableArray arrayWithCapacity:ammount];
    allAccountsB = [NSMutableArray arrayWithCapacity:ammount];

    for (uint64_t i = 0; i < ammount; i++) {
        const Gender g = (getRandom(0, 1) == 0 ? GenderMale : GenderFemale);
        const unsigned age = (unsigned)getRandom(18, 120);
        const int64_t balance = (int64_t)getRandom(0, 200000000) - 100000000;

        NSArray *const nameArray = (g == GenderMale ? maleNames : femaleNames);
        const NSUInteger nameIndex = (NSUInteger)getRandom(0, nameCount - 1);
        NSString *const name = nameArray[nameIndex];

        AccountA *const accountA = [[AccountA alloc]
            initWithName:name age:age gender:g balance:balance
        ];
        AccountB *const accountB = [[AccountB alloc]
            initWithName:name age:age gender:g balance:balance
        ];

        [allAcocuntsA addObject:accountA];
        [allAccountsB addObject:accountB];
    }
}


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        @autoreleasepool {
            NSUInteger ammount = 1000000; // 1 Million;
            if (argc > 1) {
                unsigned long long temp = 0;
                if (1 == sscanf(argv[1], "%llu", &temp)) {
                    // NSUIntegerMax may just be UINT32_MAX!
                    ammount = (NSUInteger)MIN(temp, NSUIntegerMax);
                }
            }
            createAccounts(ammount);
        }

        // Sort A and take time
        const CFAbsoluteTime startTime1 = CFAbsoluteTimeGetCurrent();
        @autoreleasepool {
            [allAcocuntsA sortedArrayUsingSelector:@selector(compare:)];
        }
        const CFAbsoluteTime runTime1 = CFAbsoluteTimeGetCurrent() - startTime1;

        // Sort B and take time
        const CFAbsoluteTime startTime2 = CFAbsoluteTimeGetCurrent();
        @autoreleasepool {
            [allAccountsB sortedArrayUsingSelector:@selector(compare:)];
        }
        const CFAbsoluteTime runTime2 = CFAbsoluteTimeGetCurrent() - startTime2;

        NSLog(@"runTime 1: %f", runTime1);
        NSLog(@"runTime 2: %f", runTime2);
    }
    return 0;
}



@implementation AccountA
    - (NSComparisonResult)compare:(nonnull AccountA *const)account {
        // Sort by gender first! Females prior to males.
        if (self.gender != account.gender) {
            if (self.gender == GenderFemale) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // Otherwise sort by name
        if (![self.name isEqualToString:account.name]) {
            return [self.name compare:account.name];
        }

        // Otherwise sort by age, young to old
        if (self.age != account.age) {
            if (self.age < account.age) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // Last ressort, sort by balance, low to high
        if (self.balance != account.balance) {
            if (self.balance < account.balance) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // If we get here, the are really equal!
        return NSOrderedSame;
    }

    - (nonnull instancetype)initWithName:(nonnull NSString *const)name
        age:(const unsigned)age gender:(const Gender)gender
        balance:(const int64_t)balance
    {
        self = [super init];
        assert(self); // We promissed to never return nil!

        _age = age;
        _gender = gender;
        _balance = balance;
        _name = [name copy];

        return self;
    }
@end


@implementation AccountB
    - (NSComparisonResult)compare:(nonnull AccountA *const)account {
        // Sort by gender first! Females prior to males.
        if (_gender != account.gender) {
            if (_gender == GenderFemale) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // Otherwise sort by name
        if (![_name isEqualToString:account.name]) {
            return [_name compare:account.name];
        }

        // Otherwise sort by age, young to old
        if (_age != account.age) {
            if (_age < account.age) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // Last ressort, sort by balance, low to high
        if (_balance != account.balance) {
            if (_balance < account.balance) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // If we get here, the are really equal!
        return NSOrderedSame;
    }

    - (nonnull instancetype)initWithName:(nonnull NSString *const)name
        age:(const unsigned)age gender:(const Gender)gender
        balance:(const int64_t)balance
    {
        self = [super init];
        assert(self); // We promissed to never return nil!

        _age = age;
        _gender = gender;
        _balance = balance;
        _name = [name copy];

        return self;
    }
@end

3
अत्यधिक जानकारीपूर्ण और डाउन-टू-अर्थ स्पष्टीकरण। कोड सैंपल के लिए
अपवोट

1
आपके पोस्ट में मेरे द्वारा देखे जाने वाले प्रमुख क्वालिफायर में से एक "... महत्वपूर्ण कोड पथ से है।" मुद्दा यह है कि उस कोड का उपयोग करें जो कोड को पढ़ने / लिखने में आसान बनाता है और फिर आपको जो महत्वपूर्ण मार्ग दिखाई देता है उसे अनुकूलित करें। इससे जहां जरूरत होगी वहां जटिलता बढ़ेगी।
सैंडी चैपमैन

1
@ViktorLexington मेरे कोड में मैं एक सेट कर रहा था unsigned intजिसे कभी भी बरकरार / जारी नहीं किया गया है, चाहे आप एआरसी का उपयोग करें या नहीं। अनुरक्षण / रिलीज स्वयं महंगा है, इसलिए अंतर कम होगा क्योंकि अनुरक्षण प्रबंधन एक स्थिर ओवरहेड जोड़ता है जो हमेशा मौजूद होता है, सीधे सेटर / गेट्टर या आइवर का उपयोग करके; अगर आप सीधे आइवर तक पहुंचते हैं, तो भी आप एक अतिरिक्त विधि कॉल के ओवरहेड को बचाएंगे। अधिकांश मामलों में कोई बड़ी बात नहीं है, जब तक कि आप ऐसा नहीं कर रहे हैं कि कई हजार बार एक दूसरे। Apple का कहना है कि डिफ़ॉल्ट रूप से गेटर्स / सेटर का उपयोग करें, जब तक कि आप एक इनिट / डीलॉक विधि में न हों या एक अड़चन न हो।
मेकी

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

2
@malhal के रूप में चिह्नित एक संपत्ति हर बार जब आप इसे एक्सेस करते हैं, तो इसके मूल्य की एक प्रति नहींcopy होगी । के गेटर संपत्ति एक की गेटर की तरह है / संपत्ति। यह मूल रूप से कोड है । केवल सेटर मूल्य को कॉपी करता है और यह मोटे तौर पर इस तरह दिखेगा , जबकि एक / सेटर इस तरह दिखता है:copystrongretainreturn [[self->value retain] autorelease];[self->value autorelease]; self->value = [newValue copy];strongretain[self->value autorelease]; self->value = [newValue retain];
Mecki

9

सबसे महत्वपूर्ण कारण सूचना छिपाने की ओओपी अवधारणा है : यदि आप गुणों के माध्यम से सब कुछ उजागर करते हैं और इस प्रकार बाहरी वस्तुओं को किसी अन्य वस्तु के आंतरिक में झांकने की अनुमति देते हैं तो आप इन आंतरिक का उपयोग करेंगे और इस प्रकार कार्यान्वयन को बदलने में जटिल होंगे।

"न्यूनतम प्रदर्शन" लाभ जल्दी से योग कर सकता है और फिर एक समस्या बन सकता है। अनुभव से जानता हूं; मैं एक ऐसे ऐप पर काम करता हूं जो वास्तव में iDevices को उनकी सीमा तक ले जाता है और हमें इस प्रकार अनावश्यक विधि कॉल (निश्चित रूप से केवल जहां संभव हो) से बचने की आवश्यकता है। इस लक्ष्य की सहायता के लिए, हम डॉट सिंटैक्स से भी बच रहे हैं क्योंकि यह पहली नज़र में विधि कॉल की संख्या को देखना मुश्किल बनाता है: उदाहरण के लिए, अभिव्यक्ति को self.image.size.widthट्रिगर करने के लिए कितने विधि कॉल हैं ? इसके विपरीत, आप तुरंत बता सकते हैं [[self image] size].width

इसके अलावा, सही आइवर नामकरण के साथ, केवीओ गुणों के बिना संभव है (आईआईआरसी, मैं केवीओ विशेषज्ञ नहीं हूं)।


3
+1 "न्यूनतम प्रदर्शन" के बारे में अच्छी प्रतिक्रिया जोड़ना और स्पष्ट रूप से सभी विधि कॉलों को देखना चाहते हैं। गुणों के साथ डॉट सिंटैक्स का उपयोग करना निश्चित रूप से बहुत सारे कामों को मास्क करता है जो कस्टम गेटर्स / सेटर्स में जाता है (विशेषकर यदि वह गेट्टर हर बार उसके नाम से किसी चीज की कॉपी लौटाता है)।
सैम

1
KVO एक सेटर का उपयोग किए बिना मेरे लिए काम नहीं करता है। Ivar को बदलना सीधे पर्यवेक्षक को नहीं कहता है कि मान बदल गया है!
बिनियन

2
KVC ivars तक पहुँच सकता है। KVO, ivars में परिवर्तन का पता नहीं लगा सकता (और इसके बजाय एक्सेसर्स पर निर्भर करता है जिसे कहा जाता है)।
निकोलाई रुहे

9

शब्दार्थ

  • क्या @propertyव्यक्त कर सकते हैं कि ivars नहीं कर सकते: nonatomicऔर copy
  • Ivars क्या व्यक्त कर @propertyसकते हैं:

प्रदर्शन

लघु कहानी: ivars तेज़ हैं, लेकिन अधिकांश उपयोगों के लिए यह कोई मायने नहीं रखता है। nonatomicगुण ताले का उपयोग नहीं करते हैं, लेकिन प्रत्यक्ष आइवर तेज है क्योंकि यह एक्सेसर्स कॉल को छोड़ देता है। विवरण के लिए lists.apple.com से निम्नलिखित ईमेल पढ़ें ।

Subject: Re: when do you use properties vs. ivars?
From: John McCall <email@hidden>
Date: Sun, 17 Mar 2013 15:10:46 -0700

गुण प्रदर्शन को कई तरह से प्रभावित करते हैं:

  1. जैसा कि पहले ही चर्चा है, लोड / स्टोर इनलाइन करने की तुलना में लोड / स्टोर करने के लिए एक संदेश भेजना धीमा है

  2. लोड / स्टोर करने के लिए एक संदेश भेजना भी थोड़ा अधिक कोड है जिसे आई-कैश में रखने की आवश्यकता है: भले ही गेट्टर / सेटर ने शून्य अतिरिक्त निर्देशों को लोड / स्टोर से परे जोड़ा हो, एक ठोस आधा होगा संदेश भेजने और परिणाम को संभालने के लिए कॉल करने वाले में अतिरिक्त निर्देश दें।

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

  4. एक संदेश भेजना फ़ंक्शन के सभी मूल्यों को स्टैक तक फैलाने के लिए मजबूर करता है (या कैली-सेव रजिस्टरों में रखा जाता है, जिसका अर्थ है कि एक अलग समय में स्पिलिंग करना)।

  5. संदेश भेजने से मनमाने ढंग से दुष्प्रभाव हो सकते हैं और इसलिए

    • संकलक को गैर-स्थानीय मेमोरी के बारे में अपनी सभी मान्यताओं को रीसेट करने के लिए मजबूर करता है
    • फहराया नहीं जा सकता, डूब जाता है, फिर से आदेश दिया जाता है, तराशा जाता है, या समाप्त किया जाता है।

  6. एआरसी में, एक संदेश भेजने का परिणाम हमेशा या तो कॉलि या कॉलर द्वारा बनाए रखा जाएगा , यहां तक ​​कि +0 रिटर्न के लिए: भले ही विधि अपने परिणाम को बनाए नहीं रखती है / फिर भी, कॉल करने वाले को यह पता नहीं है और है परिणाम को ऑटोरेल्ड होने से रोकने के लिए कार्रवाई करने का प्रयास करें। इसे कभी भी समाप्त नहीं किया जा सकता क्योंकि संदेश भेजना सांख्यिकीय रूप से विश्लेषण योग्य नहीं है।

  7. एआरसी में, क्योंकि एक सेटर विधि आमतौर पर +0 पर अपना तर्क देती है, उस वस्तु के एक अनुरक्षण को "स्थानांतरित" करने का कोई तरीका नहीं है (जो, जैसा कि ऊपर चर्चा की गई है, एआरसी आमतौर पर है) हाथीदांत में, इसलिए मूल्य आमतौर पर प्राप्त करना होता है दो बार बनाए रखना / जारी करना

इसका कोई भी मतलब नहीं है कि वे हमेशा खराब हैं, निश्चित रूप से - गुणों का उपयोग करने के कई अच्छे कारण हैं। बस ध्यान रखें कि, कई अन्य भाषा सुविधाओं की तरह, वे स्वतंत्र नहीं हैं।


जॉन।


6

गुण बनाम उदाहरण चर एक व्यापार बंद है, अंत में विकल्प आवेदन के लिए नीचे आता है।

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

प्रदर्शन जबकि "समय से पहले अनुकूलन" एक बैड थिंग (टीएम) है, केवल खराब प्रदर्शन करने वाले कोड को लिखना क्योंकि आप कम से कम उतना बुरा कर सकते हैं। एक विधि कॉल के खिलाफ बहस करने के लिए इसका कठिन होना लोड या स्टोर की तुलना में अधिक महंगा है, और कम्प्यूटेशनल गहन कोड में जल्द ही लागत बढ़ जाती है।

गुणों के साथ एक स्थिर भाषा में, जैसे कि सी #, कॉलर्स / गेटर्स को अक्सर कंपाइलर द्वारा दूर अनुकूलित किया जा सकता है। हालाँकि ओबज-सी गतिशील है और इस तरह की कॉल को हटाना बहुत कठिन है।

एब्स्ट्रक्शन ओब्ज-सी में उदाहरण चर के खिलाफ एक तर्क परंपरागत रूप से स्मृति प्रबंधन रहा है। MRC उदाहरण चर के साथ, कोड को बनाए रखने / जारी करने के लिए कॉल की आवश्यकता होती है। हालांकि जीसी या एआरसी के साथ यह तर्क दूर हो जाता है, इसलिए स्मृति प्रबंधन के लिए अमूर्तता अब उदाहरण के चर के खिलाफ तर्क नहीं है ।


5

गुण आपके चरों को अन्य कक्षाओं में उजागर करते हैं। यदि आपको केवल एक चर की आवश्यकता है जो केवल उस वर्ग के सापेक्ष है जो आप बना रहे हैं, तो एक उदाहरण चर का उपयोग करें। यहाँ एक छोटा सा उदाहरण है: RSS और पार्सिंग के लिए XML कक्षाएं जैसे डेलिगेट के तरीके और इस तरह के चक्र। पार्स के प्रत्येक अलग पास के परिणाम को संग्रहीत करने के लिए NSMutableString का एक उदाहरण होना व्यावहारिक है। कोई कारण नहीं है कि एक बाहरी वर्ग को कभी भी उस स्ट्रिंग को एक्सेस या हेरफेर करने की आवश्यकता होगी। इसलिए, आप इसे हेडर या निजी रूप से घोषित करते हैं और इसे पूरे कक्षा में एक्सेस करते हैं। इसके लिए एक प्रॉपर्टी सेट करना केवल यह सुनिश्चित करने के लिए उपयोगी हो सकता है कि कोई स्मृति समस्या नहीं है, स्वप्रेरिटेबल स्ट्रिंग का उपयोग करके गेटर / सेटर्स को इनवॉइस करें।


5

पीछे की संगतता मेरे लिए एक कारक थी। मैं किसी भी उद्देश्य-सी 2.0 सुविधाओं का उपयोग नहीं कर सका क्योंकि मैं सॉफ्टवेयर और प्रिंटर ड्राइवर विकसित कर रहा था जिसे आवश्यकता के अनुसार मैक ओएस एक्स 10.3 पर काम करना था। मुझे पता है कि आपका सवाल iOS के आसपास लक्षित था, लेकिन मुझे लगा कि मैं अभी भी गुणों का उपयोग नहीं करने के लिए अपने कारण साझा करूंगा।

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