8

यह एक छोटा सा विवरण है, लेकिन हर बार जब मैं आलसी लोड करता हूं तो मैं उस पर पकड़ा जाता हूं। क्या ये दोनों विधियां स्वीकार्य हैं? क्या बेहतर है? मान लें कि चर के पास संपत्ति बरकरार है।उद्देश्य-सी में आलसी लोडिंग - क्या मुझे गेटर के भीतर से सेटटर को कॉल करना चाहिए?

विधि # 1

(AnObject *)theObject{ 
    if (theObject == nil){ 
     theObject = [[AnObject createAnAutoreleasedObject] retain]; 
    } 
    return theObject; 
} 

विधि # 2

(AnObject *)theObject{ 
    if (theObject == nil){ 
     self.theObject = [AnObject createAnAutoreleasedObject]; 
    } 
    return theObject; 
} 

सबसे पहले, मुझे यकीन है कि अगर यह एक्सेसर भीतर एक और एक्सेसर समारोह का उपयोग करने के लिए ठीक है नहीं कर रहा हूँ (क्यों नहीं दिखाई नहीं देता, हालांकि)। लेकिन ऐसा लगता है कि सेटर के माध्यम से बिना क्लास वैरिएबल को सेट करना समान रूप से खराब हो सकता है अगर सेटटर कुछ खास करता है (या अगर संपत्ति को बरकरार रखने के अलावा कुछ बदल दिया जाता है और गेटर चेक नहीं किया जाता है)।

उत्तर

17

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

# 1 के साथ, आप सेटर से परहेज कर रहे हैं और इस प्रकार, परिवर्तन को देखते हुए कुछ भी बदलाव को नहीं देख पाएगा। "देखकर", मैं विशेष रूप से कुंजी-मूल्य अवलोकन (कोको बाइंडिंग समेत) का जिक्र कर रहा हूं, जो यूआई को स्वचालित रूप से अपडेट करने के लिए केवीओ का उपयोग करता है)।

# 2 के साथ, आप परिवर्तन अधिसूचना को ट्रिगर करेंगे, यूआई को अपडेट करेंगे और अन्यथा ठीक उसी तरह जैसे कि सेटटर को बुलाया गया था।

दोनों मामलों में, यदि ऑब्जेक्ट की शुरुआत गेटटर को कॉल करती है तो आपके पास अनंत रिकर्सन की संभावना होती है। इसमें कोई भी पर्यवेक्षक परिवर्तन अधिसूचना के हिस्से के रूप में पुराने मूल्य के लिए पूछता है। ऐसा मत करो।

यदि आप किसी भी विधि का उपयोग करने जा रहे हैं, तो सावधानीपूर्वक परिणामों पर विचार करें। एक में ऐप को असंगत स्थिति में छोड़ने की क्षमता है क्योंकि किसी संपत्ति के राज्य में परिवर्तन को सूचित नहीं किया गया है और दूसरे के पास डेडलॉक की संभावना है।

पूरी तरह से इस मुद्दे से बचने के लिए बेहतर है। निचे देखो।


पर विचार करें पर (कचरा संग्रहण, मानक कोको कमांड लाइन टूल:।

#import <Foundation/Foundation.h> 

@interface Foo : NSObject 
{ 
    NSString *bar; 
} 
@property(nonatomic, retain) NSString *bar; 
@end 
@implementation Foo 
- (NSString *) bar 
{ 
    if (!bar) { 
     NSLog(@"[%@ %@] lazy setting", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); 
     [self willChangeValueForKey: @"bar"]; 
     bar = @"lazy value"; 
     [self didChangeValueForKey: @"bar"]; 
    } 
    return bar; 
} 

- (void) setBar: (NSString *) aString 
{ 
    NSLog(@"[%@ %@] setting value %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aString); 
    bar = aString; 
} 
@end 

@interface Bar:NSObject 
@end 
@implementation Bar 
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; 
{ 
    NSLog(@"[%@ %@] %@ changed\n\tchange:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), keyPath, change); 
} 
@end 

int main (int argc, const char * argv[]) { 
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 

    Foo *foo = [Foo new]; 
    Bar *observer = [Bar new]; 
    CFRetain(observer); 
    [foo addObserver:observer forKeyPath:@"bar" 
      options: NSKeyValueObservingOptionPrior | NSKeyValueObservingOptionNew 
      context:NULL]; 
    foo.bar; 
    foo.bar = @"baz"; 
    CFRelease(observer); 

    [pool drain]; 
    return 0; 
} 

यह लटका नहीं करता है यह निसृत:

2010-09-15 12:29:18.377 foobar[27795:903] [Foo bar] lazy setting 
2010-09-15 12:29:18.396 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed 
    change:{ 
    kind = 1; 
    notificationIsPrior = 1; 
} 
2010-09-15 12:29:18.397 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed 
    change:{ 
    kind = 1; 
    new = "lazy value"; 
} 
2010-09-15 12:29:18.400 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed 
    change:{ 
    kind = 1; 
    notificationIsPrior = 1; 
} 
2010-09-15 12:29:18.400 foobar[27795:903] [Foo setBar:] setting value baz 
2010-09-15 12:29:18.401 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed 
    change:{ 
    kind = 1; 
    new = baz; 
} 

आप सूची में NSKeyValueObservingOptionOld जोड़ने के लिए थे, तो अवलोकन के विकल्पों के लिए, यह बहुत लटका है।

एक टिप्पणी मैंने पहले बनाया करने के लिए वापस हो रही है; सबसे अच्छा समाधान आपके गेटर/सेटर के हिस्से के रूप में आलसी प्रारंभिक नहीं है। यह बहुत अच्छा दागदार है। आप अपने ऑब्जेक्ट ग्राफ़ स्टेटस को उच्च स्तर पर प्रबंधित करने से कहीं बेहतर हैं और इसके एक हिस्से के रूप में, एक राज्य संक्रमण है जो मूल रूप से "यो! मैं इस सबसिस्टम का उपयोग करने जा रहा हूं! खराब लड़का! " वह आलसी प्रारंभिक करता है।

+0

यह मेरी राय में सही जवाब है। –

+1

वास्तव में ऑब्जेक्ट को आलसी शुरू करते समय देखने के लिए कोई बदलाव नहीं है, इसलिए यह उत्तर ** ** सही नहीं है। – Sven

+0

हू? चाहे वह आलसी शुरू हो या न हो, अप्रासंगिक है। अगर मैंने 'ऑब्जेक्ट' के केवीओ अवलोकन को स्थापित किया है और गेटटर में 'self.theObject = ...' रखा है, तो केवी अवलोकन ** ** आग लगेगा। "आलसी प्रारंभिक" सिर्फ एक पैटर्न के लिए एक नाम है; न तो संकलक और न ही रनटाइम इसके बारे में कुछ भी जानता है। – bbum

0

दोनों मूल रूप से समान हैं, यह वास्तव में आपके लिए यह चुनने के लिए कि आपके मामले के लिए कौन सा सबसे अच्छा है। आपने पहले ही संपत्ति सिंटैक्स का उपयोग करने के बारे में पेशेवरों/विपक्ष का वर्णन किया है।

+0

नहीं, वे नहीं हैं। # 2 गलत है! – Sven

1

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

+0

यह स्मृति प्रबंधन बिंदु दृश्य से समान हो सकता है। लेकिन सेटर्स सिर्फ मेमोरी मैनेजमेंट की देखभाल करने से ज्यादा करते हैं, इसके बारे में सोचने के लिए केवीओ भी है। और यह विधि # 2 गलत बनाता है! – Sven

3

इन तरीकों में समान कभी नहीं कर रहे हैं। पहला एक सही है, जबकि दूसरा एक गलत है! एक गेटर कभी will/didChangeValueForKey: पर कॉल नहीं कर सकता है और इसलिए सेटटर भी नहीं। अगर उस संपत्ति को देखा जाता है तो इससे अनंत रिकर्सन हो जाएगा।

और इसके अलावा, वहाँ जब सदस्य आरंभ नहीं हो जाता निरीक्षण करने के लिए कोई राज्य परिवर्तन है। आप अपने ऑब्जेक्ट को theObject के लिए पूछते हैं और आपको यह मिलता है। जब यह बनाया जाता है तो कार्यान्वयन विस्तार होता है और बाहरी दुनिया के लिए कोई चिंता नहीं होती है।

+0

अगर गेटटर को कॉल से पहले अस्तित्व में पर्यवेक्षक हैं तो अवलोकन अधिसूचनाओं को ट्रिगर किए बिना गेटर संशोधित स्थिति होने से एप्लिकेशन को असंगत स्थिति में छोड़ दिया जाएगा। – bbum

+0

दूसरे शब्दों में, दोनों विधियां गलत हैं .... – bbum

+1

नहीं, बाइंडिंग सेट करना हमेशा गेटर को कॉल करेगा। पर्यवेक्षक को असंगत स्थिति में लाने का कोई तरीका नहीं है। – Sven

संबंधित मुद्दे