NSString प्रॉपर्टी: कॉपी या रिटेन?


331

मान लीजिए कि मेरे पास SomeClassएक stringप्रॉपर्टी नाम के साथ एक वर्ग है :

@interface SomeClass : NSObject
{
    NSString* name;
}

@property (nonatomic, retain) NSString* name;

@end

मैं समझता हूं कि नाम को उस NSMutableStringस्थिति में सौंपा जा सकता है जिस स्थिति में यह गलत व्यवहार हो सकता है।

  • सामान्य रूप से तार के लिए, क्या हमेशाcopy इसके बजाय विशेषता का उपयोग करना एक अच्छा विचार है retain?
  • किसी भी तरह से "नकल" संपत्ति ऐसी "रिटेन-एड" संपत्ति से कम कुशल है?

6
अनुवर्ती प्रश्न: nameजारी किया जाना चाहिए deallocया नहीं?
चेतन

7
@ चेतन हां यह चाहिए!
जॉन

जवाबों:


440

उन विशेषताओं के लिए जिनका प्रकार एक अपरिवर्तनीय मूल्य वर्ग है जो NSCopyingप्रोटोकॉल के अनुरूप है , आपको लगभग हमेशा copyअपनी @propertyघोषणा में निर्दिष्ट करना चाहिए । निर्दिष्ट करना retainऐसी चीज है जिसे आप लगभग ऐसी स्थिति में कभी नहीं चाहते हैं।

यहाँ आप ऐसा क्यों करना चाहते हैं:

NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];

Person *p = [[[Person alloc] init] autorelease];
p.name = someName;

[someName setString:@"Debajit"];

Person.nameसंपत्ति के वर्तमान मूल्य अलग-अलग होंगे, इस आधार पर कि संपत्ति घोषित की गई है retainया नहीं copy- यह होगा @"Debajit"यदि संपत्ति को चिह्नित किया गया है retain, लेकिन @"Chris"अगर संपत्ति को चिह्नित किया गया है copy

चूंकि लगभग सभी मामलों में आप किसी ऑब्जेक्ट की विशेषताओं को उसकी पीठ के पीछे उत्परिवर्तन को रोकना चाहते हैं , इसलिए आपको उनका प्रतिनिधित्व करने वाले गुणों को चिह्नित करना चाहिए copy। (और तुम अपने आप को सेटर बारे में अगर उपयोग करने के बजाय @synthesizeआप वास्तव में उपयोग करने के लिए याद रखना चाहिए copyकी बजाय retainउस में।)


61
इस उत्तर के कारण कुछ भ्रम हो सकता है (देखें robnapier.net/blog/implementing-nscopying-439#comment-1312 )। आप NSString के बारे में बिल्कुल सही हैं, लेकिन मेरा मानना ​​है कि आपने बिंदु को थोड़ा सामान्य कर दिया है। NSString को कॉपी किए जाने का कारण यह है कि इसमें एक सामान्य परिवर्तनशील उपवर्ग (NSMutableString) है। उन वर्गों के लिए, जिनके पास एक परस्पर उपवर्ग नहीं है (विशेष रूप से कक्षाएं जो आप स्वयं लिखते हैं), आमतौर पर समय और स्मृति को बर्बाद करने से बचने के लिए प्रतिलिपि के बजाय उन्हें बनाए रखना बेहतर होता है।
रोब नेपियर

63
आपका तर्क गलत है। आपको समय / स्मृति के आधार पर प्रतिलिपि बनाने या बनाए रखने का निर्धारण नहीं करना चाहिए, आपको यह निर्धारित करना चाहिए कि वांछित शब्दार्थ के आधार पर। इसलिए मैंने विशेष रूप से अपने जवाब में "अपरिवर्तनीय मूल्य वर्ग" शब्द का इस्तेमाल किया। यह भी कोई बात नहीं है कि क्या एक वर्ग में परिवर्तनशील उपवर्ग होते हैं, या स्वयं ही परिवर्तनशील होते हैं।
क्रिस हैनसन

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

5
@Phil नैश: मैं आपके लिए अकेले और दूसरों के साथ साझा की जाने वाली परियोजनाओं के लिए विभिन्न शैलियों का उपयोग करने के लिए एक कोड गंध पर विचार करूंगा। हर भाषा / फ्रेमवर्क में सामान्य नियम या शैलियाँ होती हैं, जिन पर डेवलपर्स सहमत होते हैं। निजी परियोजनाओं में उनकी उपेक्षा करना गलत लगता है। और आपके तर्क के लिए "मेरे कोड में, मैं उत्परिवर्ती स्ट्रिंग्स नहीं लौटा रहा हूं": यह आपके अपने स्ट्रिंग्स के लिए काम कर सकता है, लेकिन आप कभी भी उन स्ट्रिंग्स के बारे में नहीं जानते हैं जो आपको फ्रेमवर्क से प्राप्त होते हैं।
निकोलाई रुहे

7
@Nikolai मैं सिर्फ NSMutableStringएक क्षणिक "स्ट्रिंग बिल्डर" प्रकार के अलावा (जिसमें से मैं तुरंत एक अप्रचलित प्रति ले लेता हूं) को छोड़कर उपयोग नहीं करता । मैं उन्हें विवेकपूर्ण प्रकारों के लिए पसंद करूंगा - लेकिन मैं यह अनुमति दूंगा कि यदि कोई वास्तविक स्ट्रिंग मेरी चिंता का विषय नहीं है, तो प्रतिलिपि बनाए रखने के लिए स्वतंत्र है।
philsquared

120

NSString के लिए कॉपी का उपयोग किया जाना चाहिए। अगर यह Mutable है, तो यह कॉपी हो जाता है। यदि यह नहीं है, तो यह अभी भी बरकरार है। सटीक रूप से शब्दार्थ जिसे आप एक ऐप में चाहते हैं (टाइप वही करें जो सबसे अच्छा है)।


1
मैं अभी भी परिवर्तनशील और अपरिवर्तनीय रूपों को प्राथमिकता देना पसंद करूंगा, लेकिन मुझे इस बात का एहसास नहीं था कि मूल स्ट्रिंग अपरिवर्तनीय है - जो कि वहां सबसे ज्यादा है। धन्यवाद।
philsquared

25
+1 उस NSStringसंपत्ति का उल्लेख करने के लिए जिसे वैसे भी घोषित किया copyजाएगा retain(यदि यह अपरिवर्तनीय है, तो निश्चित रूप से)। अन्य उदाहरण मैं सोच सकता हूं NSNumber
मटमैला

इस उत्तर और नीचे दिए गए अंतर के बीच @GBY ने किसे वोट दिया?
गैरी लिन

67

सामान्य रूप से तार के लिए, क्या हमेशा बनाए रखने के बजाय कॉपी विशेषता का उपयोग करना एक अच्छा विचार है?

हां - सामान्य रूप से हमेशा कॉपी विशेषता का उपयोग करें।

ऐसा इसलिए है क्योंकि आपकी NSString संपत्ति को NSString उदाहरण या NSMutableString उदाहरण दिया जा सकता है , और इसलिए हम वास्तव में यह निर्धारित नहीं कर सकते हैं कि पारित किया जा रहा मूल्य एक अपरिवर्तनीय या परिवर्तनशील वस्तु है या नहीं।

किसी भी तरह से "नकल" संपत्ति ऐसी "रिटेन-एड" संपत्ति से कम कुशल है?

  • यदि आपकी संपत्ति को NSString उदाहरण दिया जा रहा है , तो उत्तर " नहीं " है - प्रतिलिपि बनाए रखने से कम कुशल नहीं है।
    (यह कम कुशल नहीं है क्योंकि एनएसएसट्रिंग वास्तव में कॉपी प्रदर्शन नहीं करने के लिए पर्याप्त स्मार्ट है।)

  • यदि आपकी संपत्ति एक NSMutableString उदाहरण से गुज़री है, तो इसका उत्तर " हाँ " है - प्रतिलिपि बनाए रखने की तुलना में कम कुशल है।
    (यह कम कुशल है क्योंकि एक वास्तविक मेमोरी आवंटन और कॉपी होनी चाहिए, लेकिन यह शायद एक वांछनीय बात है।)

  • आम तौर पर "कॉपी" संपत्ति बोलने से कम कुशल होने की क्षमता होती है - हालांकि NSCopyingप्रोटोकॉल के उपयोग के माध्यम से , एक वर्ग को लागू करना संभव है जो "बस के रूप में कुशल" है कॉपी करने के लिए जैसा कि इसे बनाए रखना है। NSString उदाहरण इसका एक उदाहरण है।

आम तौर पर (केवल एनएसएसट्रिंग के लिए नहीं), मुझे "रिटेन" के बजाय "कॉपी" का उपयोग कब करना चाहिए?

copyजब आप बिना चेतावनी के संपत्ति की आंतरिक स्थिति नहीं चाहते हैं तो आपको हमेशा उपयोग करना चाहिए । अपरिवर्तनीय वस्तुओं के लिए भी - ठीक से लिखी जाने वाली अपरिवर्तनीय वस्तुएँ कुशलता से कॉपी संभालेंगी (देखें अपरिवर्तनीयता के बारे में अगला भाग NSCopying) और ।

retainऑब्जेक्ट के लिए प्रदर्शन कारण हो सकते हैं, लेकिन यह एक रखरखाव ओवरहेड के साथ आता है - आपको अपने कोड के बाहर आंतरिक स्थिति की संभावना का प्रबंधन करना चाहिए। जैसा कि वे कहते हैं - अंतिम का अनुकूलन करें।

लेकिन, मैंने अपनी कक्षा को अपरिवर्तनीय लिखा - क्या मैं इसे सिर्फ "बरकरार" नहीं रख सकता?

नहीं - उपयोग करें copy। यदि आपकी कक्षा वास्तव में अपरिवर्तनीय है, तो NSCopyingप्रोटोकॉल का उपयोग करने के लिए अपनी कक्षा को स्वयं copyउपयोग किए जाने पर वापस करने के लिए इसे लागू करना सबसे अच्छा अभ्यास है। अगर तुम यह करते हो:

  • आपके द्वारा उपयोग किए जाने पर आपकी कक्षा के अन्य उपयोगकर्ता प्रदर्शन लाभ प्राप्त करेंगे copy
  • copyएनोटेशन अपने खुद के कोड और अधिक maintainable बनाता है - copyएनोटेशन इंगित करता है कि आप वास्तव में इस वस्तु राज्य कहीं और बदलने के बारे में चिंता की जरूरत नहीं है।

39

मैं इस सरल नियम का पालन करने की कोशिश करता हूं:

  • क्या मैं उस समय वस्तु के मूल्य को पकड़ना चाहता हूं जब मैं इसे अपनी संपत्ति को सौंप रहा हूं? कॉपी का उपयोग करें ।

  • क्या मैं ऑब्जेक्ट को पकड़ना चाहता हूं और मुझे परवाह नहीं है कि वर्तमान में इसके आंतरिक मूल्य क्या हैं या भविष्य में होंगे? मजबूत (बनाए रखने) का उपयोग करें ।

उदाहरण के लिए: क्या मैं "लिसा मिलर" ( कॉपी ) नाम पर पकड़ बनाना चाहता हूं या मैं लिसा मिलर ( मजबूत ) व्यक्ति को पकड़ना चाहता हूं ? उसका नाम बाद में "लिसा स्मिथ" में बदल सकता है, लेकिन वह अभी भी वही व्यक्ति होगा।


14

इस उदाहरण के माध्यम से कॉपी और रिटेन को समझाया जा सकता है:

NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];

Person *p = [[[Person alloc] init] autorelease];
p.name = someName;

[someName setString:@"Debajit"];

यदि संपत्ति टाइप कॉपी की है, तो

[Person name]स्ट्रिंग की सामग्री रखने वाली स्ट्रिंग के लिए एक नई प्रति बनाई जाएगी someName। अब someNameस्ट्रिंग पर किसी भी ऑपरेशन का कोई प्रभाव नहीं पड़ेगा [Person name]

[Person name]और someNameस्ट्रिंग्स में अलग-अलग मेमोरी एड्रेस होंगे।

लेकिन बनाए रखने के मामले में,

दोनों [Person name]somename string के समान मेमोरी एड्रेस को होल्ड करेंगे, बस somename string के रिटेन काउंट को 1 से बढ़ाना होगा।

तो somename स्ट्रिंग में कोई भी परिवर्तन स्ट्रिंग में परिलक्षित होगा [Person name]


3

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

p.name = [someName copy];

बेशक, उस संपत्ति को डिजाइन करते समय, जिसमें केवल वह संपत्ति होती है, केवल आपको पता चल जाएगा कि क्या डिजाइन उस पैटर्न से लाभान्वित होता है जहां असाइनमेंट प्रतियां लेती हैं - Cocoawithlove.com के पास कहने के लिए निम्नलिखित हैं:

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

//person object has details of an individual you're assigning to a contact list.

Contact *contact = [[[Contact alloc] init] autorelease];
contact.name = person.name;

//person changes name
[[person name] setString:@"new name"];
//now both person.name and contact.name are in sync.

इस मामले में, कॉपी का उपयोग किए बिना, हमारी संपर्क वस्तु नए मूल्य को स्वचालित रूप से लेती है; यदि हमने इसका उपयोग किया है, हालांकि, हमें मैन्युअल रूप से यह सुनिश्चित करना होगा कि परिवर्तनों का पता लगाया गया और समन्वयित किया गया। इस मामले में, शब्दार्थ को बनाए रखना वांछनीय हो सकता है; दूसरे में, प्रतिलिपि अधिक उपयुक्त हो सकती है।


1
@interface TTItem : NSObject    
@property (nonatomic, copy) NSString *name;
@end

{
    TTItem *item = [[TTItem alloc] init];    
    NSString *test1 = [NSString stringWithFormat:@"%d / %@", 1, @"Go go go"];  
    item.name = test1;  
    NSLog(@"-item.name: point = %p, content = %@; test1 = %p", item.name, item.name, test1);  
    test1 = [NSString stringWithFormat:@"%d / %@", 2, @"Back back back"];  
    NSLog(@"+item.name: point = %p, content = %@, test1 = %p", item.name, item.name, test1);
}

Log:  
    -item.name: point = 0x9a805a0, content = 1 / Go go go; test1 = 0x9a805a0  
    +item.name: point = 0x9a805a0, content = 1 / Go go go, test1 = 0x9a84660

0

NSString संपत्ति घोषित करने के लिए आपको हर समय कॉपी का उपयोग करना चाहिए

@property (nonatomic, copy) NSString* name;

आपको इसे अधिक जानकारी के लिए पढ़ना चाहिए कि क्या यह अपरिवर्तनीय स्ट्रिंग लौटाता है (यदि मामले में परस्पर स्ट्रिंग पारित हो गई थी) या एक बरकरार स्ट्रिंग लौटाता है (यदि अपरिवर्तनीय स्ट्रिंग पारित हो गई थी)

NSCopying प्रोटोकॉल संदर्भ

वर्ग और इसकी सामग्री अपरिवर्तनीय होने पर एक नई प्रति बनाने के बजाय मूल को बनाए रखकर NSCopying को लागू करें

मूल्य वस्तुओं

तो, हमारे अपरिवर्तनीय संस्करण के लिए, हम ऐसा कर सकते हैं:

- (id)copyWithZone:(NSZone *)zone
{
    return self;
}

-1

चूँकि नाम एक (अपरिवर्तनीय) है NSString, यदि आप किसी अन्य NSStringनाम को सेट करते हैं तो प्रतिलिपि या बनाए रखने से कोई फ़र्क नहीं पड़ता है । दूसरे शब्द में, कॉपी एक के द्वारा संदर्भ गणना को बढ़ाते हुए, बनाए रखने की तरह व्यवहार करता है। मुझे लगता है कि अपरिवर्तनीय वर्गों के लिए यह एक स्वचालित अनुकूलन है, क्योंकि वे अपरिवर्तनीय हैं और क्लोन करने की कोई आवश्यकता नहीं है। लेकिन जब NSMutalbeString mstrनाम पर सेट किया जाता है, तो mstrसही की खातिर वसीयत की सामग्री की नकल की जाएगी।


1
आप घोषित प्रकार को वास्तविक प्रकार के साथ भ्रमित कर रहे हैं। यदि आप "रिटेन" संपत्ति का उपयोग करते हैं और एक NSMutableString असाइन करते हैं, तो NSMutableString को बरकरार रखा जाएगा, लेकिन फिर भी इसे संशोधित किया जा सकता है। यदि आप "कॉपी" का उपयोग करते हैं, तो जब आप NSMutableString असाइन करते हैं तो एक अपरिवर्तनीय प्रतिलिपि बनाई जाएगी; तब से संपत्ति पर "प्रतिलिपि" बस बरकरार रहेगी, क्योंकि उत्परिवर्तनीय स्ट्रिंग की प्रतिलिपि स्वयं अपरिवर्तनीय है।
gnasher729

1
आप यहाँ कुछ महत्वपूर्ण तथ्यों को याद कर रहे हैं, यदि आप किसी ऐसी वस्तु का उपयोग करते हैं जो कि एक परिवर्तनशील चर से आया है, जब वह चर संशोधित हो जाता है, तो क्या आपकी वस्तु, यदि वह एक प्रतिलिपि चर से आई है, तो आपकी वस्तु में चर का वर्तमान मूल्य होगा नहीं बदलेगा
ब्रायन पी

-1

यदि स्ट्रिंग बहुत बड़ी है तो प्रतिलिपि प्रदर्शन को प्रभावित करेगी और बड़े स्ट्रिंग की दो प्रतियां अधिक मेमोरी का उपयोग करेंगी।

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