NSArray की प्रतिलिपि बनाना


119

क्या कोई अंतर्निहित कार्य है जो मुझे गहरी कॉपी करने की अनुमति देता है NSMutableArray?

मैंने चारों ओर देखा, कुछ लोग कहते हैं [aMutableArray copyWithZone:nil]कि गहरी नकल के रूप में काम करता है। लेकिन मैंने कोशिश की और यह उथली प्रति प्रतीत हो रही है।

अभी मैं स्वयं एक forलूप के साथ कॉपी कर रहा हूं :

//deep copy a 9*9 mutable array to a passed-in reference array

-deepMuCopy : (NSMutableArray*) array 
    toNewArray : (NSMutableArray*) arrayNew {

    [arrayNew removeAllObjects];//ensure it's clean

    for (int y = 0; y<9; y++) {
        [arrayNew addObject:[NSMutableArray new]];
        for (int x = 0; x<9; x++) {
            [[arrayNew objectAtIndex:y] addObject:[NSMutableArray new]];

            NSMutableArray *aDomain = [[array objectAtIndex:y] objectAtIndex:x];
            for (int i = 0; i<[aDomain count]; i++) {

                //copy object by object
                NSNumber* n = [NSNumber numberWithInt:[[aDomain objectAtIndex:i] intValue]];
                [[[arrayNew objectAtIndex:y] objectAtIndex:x] addObject:n];
            }
        }
    }
}

लेकिन मैं एक क्लीनर, अधिक रसीला समाधान चाहूंगा।


44
@Genericrich गहरी और उथली प्रतियां सॉफ्टवेयर विकास में बहुत अच्छी तरह से परिभाषित शब्द हैं। Google.com मदद कर सकता है
एंड्रयू ग्रांट

1
शायद कुछ भ्रम की स्थिति इसलिए है क्योंकि -copyमैक ओएस एक्स 10.4 और 10.5 के बीच अपरिवर्तनीय संग्रह का व्यवहार बदल गया है: developer.apple.com/library/mac/releasenotes/Cocoa/ ... ("अपरिवर्तनीय संग्रह और कॉपी व्यवहार के लिए नीचे स्क्रॉल करें")
user102008

1
@AndrewGrant आगे के विचार पर, और सम्मान के साथ, मैं असहमत हूं कि गहरी प्रति एक अच्छी तरह से परिभाषित शब्द है। आप किस स्रोत को पढ़ते हैं, इस पर निर्भर करते हुए, यह स्पष्ट नहीं है कि नेस्टेड डेटा संरचनाओं में असीमित पुनरावृत्ति एक 'डीप कॉपी' ऑपरेशन की आवश्यकता है। दूसरे शब्दों में, आपको इस बात पर परस्पर विरोधी जवाब मिलेगा कि क्या एक कॉपी ऑपरेशन जो एक नई वस्तु बनाता है जिसके सदस्य मूल वस्तु के सदस्यों की उथली प्रतियां हैं, एक 'डीप कॉपी' ऑपरेशन है या नहीं। इसके बारे में कुछ चर्चा के लिए stackoverflow.com/a/6183597/1709587 देखें (एक जावा संदर्भ में, लेकिन यह सभी समान है)।
मार्क अमेरी

@AndrewGrant मुझे @MarkAmery और @Genericrich का बैकअप लेना होगा। एक गहरी प्रतिलिपि को अच्छी तरह से परिभाषित किया गया है यदि एक संग्रह में रूट क्लास और उसके सभी तत्वों का उपयोग किया जाता है। NSArray (और अन्य objc संग्रह) के साथ ऐसा नहीं है। यदि कोई तत्व लागू नहीं होता है copy, तो "गहरी प्रति" में क्या डाला जाएगा? यदि तत्व एक और संग्रह है, copyतो वास्तव में एक प्रति (एक ही वर्ग) की उपज नहीं होती है। इसलिए मुझे लगता है कि विशिष्ट मामले में वांछित कॉपी के प्रकार के बारे में बहस करना पूरी तरह से वैध है।
निकोलाई रुहे

@NikolaiRuhe यदि कोई तत्व लागू नहीं करता NSCopying/ करती है -copy, तो यह प्रतिलिपि योग्य नहीं है - इसलिए आपको इसकी प्रतिलिपि बनाने की कोशिश कभी नहीं करनी चाहिए, क्योंकि यह एक ऐसी क्षमता नहीं है जिसे इसे डिजाइन किया गया था। कोको के कार्यान्वयन के संदर्भ में, गैर-प्रतिलिपि योग्य वस्तुओं में अक्सर कुछ सी बैकएंड राज्य होते हैं जो वे बंधे होते हैं, इसलिए ऑब्जेक्ट की डायरेक्ट-कॉपी हैक करने से दौड़ की स्थिति या इससे भी बदतर हो सकती है। तो जवाब देने के लिए "गहरी प्रतिलिपि में क्या रखा जाएगा" - एक बनाए रखा रेफरी। केवल एक चीज जिसे आप कहीं भी रख सकते हैं जब आपके पास कोई गैर- NSCopyingवस्तु हो।
स्लिप डी। थॉम्पसन

जवाबों:


210

जैसा कि गहरी प्रतियों के बारे में Apple प्रलेखन स्पष्ट रूप से बताता है:

यदि आपको केवल एक-स्तरीय-गहरी प्रतिलिपि की आवश्यकता है:

NSMutableArray *newArray = [[NSMutableArray alloc] 
                             initWithArray:oldArray copyItems:YES];

उपरोक्त कोड एक नई सरणी बनाता है जिसके सदस्य पुराने सरणी के सदस्यों की उथली प्रतियां हैं।

ध्यान दें कि यदि आपको संपूर्ण नेस्टेड डेटा संरचना को गहराई से कॉपी करने की आवश्यकता है - क्या लिंक किए गए Apple डॉक्स एक सच्ची गहरी कॉपी कहते हैं - तो यह दृष्टिकोण पर्याप्त नहीं होगा। कृपया इसके लिए अन्य उत्तर यहां देखें।


सही उत्तर लगता है। एपीआई बताता है कि प्रत्येक तत्व को एक [तत्व copyWithZone:] संदेश मिलता है, जो कि आप जो देख रहे थे वह हो सकता है। यदि आप वास्तव में यह देख रहे हैं कि [NSMutableArray copyWithZone: nil] भेजना गहरी कॉपी नहीं है, तो सरणियों का एक सरणी इस पद्धति का उपयोग करके सही तरीके से कॉपी नहीं हो सकता है।
एड मार्टी

7
मुझे नहीं लगता कि यह उम्मीद के मुताबिक काम करेगा। Apple डॉक्स से: "copyWithZone: पद्धति एक उथली प्रतिलिपि का प्रदर्शन करती है । यदि आपके पास मनमानी गहराई का संग्रह है, तो ध्वज पैरामीटर के लिए YES पास करना सतह के नीचे पहले स्तर की अपरिवर्तनीय प्रतिलिपि का प्रदर्शन करेगा। यदि आप कोई उत्परिवर्तन नहीं करते हैं। पहला स्तर अप्रभावित है। किसी भी स्थिति में, सभी गहन स्तरों की उत्परिवर्तन अप्रभावित है। "SO प्रश्न गहरी उत्परिवर्ती प्रतियों की चिंता करता है।
जो डी 'एंड्रिया

7
यह एक गहरी प्रति नहीं है
डेयज-डीजे

9
यह एक अधूरा जवाब है। इससे एक स्तरीय गहरी प्रति प्राप्त होती है। यदि ऐरे के भीतर अधिक जटिल प्रकार हैं तो यह एक गहरी प्रतिलिपि प्रदान नहीं करेगा।
कैमरन लोवेल पामर

7
यह एक गहरी प्रतिलिपि हो सकती है, जो इस बात पर निर्भर करती है कि copyWithZone:प्राप्त वर्ग पर कैसे लागू किया जाता है।
devios1

62

जिस तरह से मुझे आसानी से पता है कि यह संग्रह करना है और फिर तुरंत अपने सरणी को अनारकली करें। यह एक हैक की तरह महसूस होता है, लेकिन वास्तव में स्पष्ट रूप से कॉपी डॉक्युमेंट्स पर Apple डॉक्यूमेंटेशन में सुझाव दिया गया है , जिसमें कहा गया है:

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

लिस्टिंग 3 एक सच्ची गहरी कॉपी

NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
          [NSKeyedArchiver archivedDataWithRootObject:oldArray]];

पकड़ यह है कि आपकी वस्तु को NSCoding इंटरफ़ेस का समर्थन करना चाहिए, क्योंकि इसका उपयोग डेटा को संग्रहीत / लोड करने के लिए किया जाएगा।

स्विफ्ट 2 संस्करण:

let trueDeepCopyArray = NSKeyedUnarchiver.unarchiveObjectWithData(
    NSKeyedArchiver.archivedDataWithRootObject(oldArray))

12
यदि आपके सरणियाँ बड़ी हैं, तो NSArchiver और NSUnarchiver का उपयोग करना एक बहुत ही भारी समाधान है। एक सामान्य NSArray श्रेणी की विधि लिखना, जो NSCopying प्रोटोकॉल का उपयोग करता है, ट्रिक करेगा, जिससे अपरिवर्तनीय वस्तुओं का एक सरल 'रिटेन' और उत्परिवर्तित लोगों की एक वास्तविक 'कॉपी' बन जाएगा।
निकिता झुक

खर्च के बारे में सतर्क रहना अच्छा है, लेकिन क्या एनएससी कोडिंग initWithArray:copyItems:पद्धति में उपयोग किए जाने वाले एनएसकॉपिंग की तुलना में वास्तव में अधिक महंगा होगा ? यह संग्रह / अनअर्काइविंग वर्कअराउंड बहुत उपयोगी लगता है, यह देखते हुए कि कितने नियंत्रण वर्ग NSCoding के अनुरूप हैं, लेकिन NSCopying के लिए नहीं।
वीनके

मैं अत्यधिक अनुशंसा करता हूं कि आप इस दृष्टिकोण का उपयोग कभी न करें। मेमोरी को कॉपी करने की तुलना में सीरियलाइजेशन कभी तेज नहीं होता है।
ब्रेट

1
यदि आपके पास कस्टम ऑब्जेक्ट हैं, तो NSCoding प्रोटोकॉल का पालन करने के लिए EncodeWithCoder और initWithCoder को लागू करना सुनिश्चित करें।
user523234

स्पष्ट रूप से क्रमांकन का उपयोग करते समय प्रोग्रामर के विवेक की दृढ़ता से सलाह दी जाती है।
VH-NZZ

34

डिफ़ॉल्ट रूप से कॉपी एक उथली प्रतिलिपि देता है

ऐसा इसलिए है क्योंकि कॉलिंग copyवही है copyWithZone:NULLजिसे डिफ़ॉल्ट क्षेत्र के साथ कॉपी करने के रूप में भी जाना जाता है। copyकॉल एक गहरी प्रतिलिपि में परिणाम नहीं करता है। ज्यादातर मामलों में यह आपको उथली प्रतिलिपि देता है, लेकिन किसी भी मामले में यह वर्ग पर निर्भर करता है। गहन चर्चा के लिए मैं Apple डेवलपर साइट पर कलेक्शंस प्रोग्रामिंग टॉपिक्स की सिफारिश करता हूं ।

initWithArray: CopyItems: एक-स्तरीय गहरी प्रतिलिपि देता है

NSArray *deepCopyArray = [[NSArray alloc] initWithArray:someArray copyItems:YES];

NSCoding Apple एक गहरी प्रतिलिपि प्रदान करने के लिए अनुशंसित तरीका है

एक सच्ची गहरी प्रति (एरे के एरे) के लिए आपको NSCodingवस्तु की आवश्यकता होगी और उसे संग्रहित / संग्रहित करना होगा:

NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

1
यह सही है। स्मृति, प्रदर्शन के बारे में लोकप्रियता या समय से पहले अनुकूलित-मामलों के बावजूद। एक तरफ के रूप में, गहरी प्रतिलिपि के लिए यह सीर-ड्रेसर हैक कई अन्य भाषा वातावरणों में उपयोग किया जाता है। जब तक ओब्ज डिडअप नहीं होता है, यह एक अच्छी गहरी कॉपी को मूल से पूरी तरह से अलग करने की गारंटी देता है।

1
यहाँ कम rottable Apple doc url developer.apple.com/library/mac/#documentation/cocoa/conceptual/…

7

डिक्टोनरी के लिए

NSMutableDictionary *newCopyDict = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)objDict, kCFPropertyListMutableContainers);

ऐरे के लिए

NSMutableArray *myMutableArray = (NSMutableArray *)CFPropertyListCreateDeepCopy(NULL, arrData, kCFPropertyListMutableContainersAndLeaves);


4

नहीं, इसके लिए रूपरेखाओं में कुछ निर्मित नहीं है। कोको संग्रह उथली प्रतियों ( copyया arrayWithArray:विधियों के साथ) का समर्थन करते हैं, लेकिन एक गहरी प्रतिलिपि अवधारणा के बारे में भी बात नहीं करते हैं।

ऐसा इसलिए होता है क्योंकि "गहरी प्रति" को परिभाषित करना मुश्किल हो जाता है क्योंकि आपके संग्रह की सामग्री आपकी स्वयं की कस्टम वस्तुओं सहित शुरू होती है। "गहरी प्रतिलिपि" इसका मतलब यह है कि हर वस्तु ग्राफ में वस्तु के लिए एक अनूठा संदर्भ रिश्तेदार है हर मूल वस्तु ग्राफ में वस्तु?

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

@ एंड्रयूग्रांट का उत्तर कुंजीयन संग्रह / अनारकली के उपयोग का सुझाव है, जो एक मनमाने ढंग से वस्तुओं के लिए इसे प्राप्त करने का एक गैर-अनुरूप लेकिन सही और स्वच्छ तरीका है। यह पुस्तक यहां तक ​​चली जाती है कि सभी वस्तुओं को एक श्रेणी जोड़ने का सुझाव दें जो वास्तव में गहरी नकल का समर्थन करने के लिए करता है।


2

यदि JSON संगत डेटा के लिए गहरी प्रतिलिपि प्राप्त करने की कोशिश कर रहा है तो मेरे पास वर्कअराउंड है।

बस JSON ऑब्जेक्ट को फिर NSDataसे NSArrayउपयोग करने NSJSONSerializationऔर फिर से बनाने के लिए, यह पूरी तरह से नई और नई प्रतिलिपि बना देगा, NSArray/NSDictionaryजिसमें उनके नए मेमोरी रेफरेंस होंगे।

लेकिन यह सुनिश्चित करें कि NSArray / NSDictionary और उनके बच्चों की वस्तुओं JSON serializable होना चाहिए।

NSData *aDataOfSource = [NSJSONSerialization dataWithJSONObject:oldCopy options:NSJSONWritingPrettyPrinted error:nil];
NSDictionary *aDictNewCopy = [NSJSONSerialization JSONObjectWithData:aDataOfSource options:NSJSONReadingMutableLeaves error:nil];

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