2013-12-11 6 views
6

NSArchiver पुनः ओएस एक्स 10.2 के बाद से हटा दिया गया है, और iOSउपवर्गीकरण NSCoder, NSArchiver

दूसरी ओर पर AFAIK उपलब्ध नहीं है, NSKeyedArchiver गति & संक्षिप्तता ओर से कमी है (some users रिपोर्ट 100 से अधिक जाना जाता है NSKeyedArchiver और NSArchiver के बीच प्रदर्शन प्रदर्शन अंतर)। जिन वस्तुओं को मैं संग्रहित करना चाहता हूं वे मुख्य रूप से NSObject उप-वर्ग हैं जिनमें NSNumber हैं, और ऑब्जेक्ट्स जिनमें आदिम प्रकार हैं (मुख्य रूप से double)। मुझे विश्वास नहीं है कि ओवरहेड कुंजी संग्रह का तात्पर्य यह है कि इसका लायक है।

इसलिए मैंने NSArchiver की शैली में एक सीरियल कोडर बनाने के लिए आईओएस पर NSCoder को उपclass करने का निर्णय लिया।

मैं समझता हूं कि कुंजी वाले अभिलेखागार आसानी से कहां आ सकते हैं: पिछड़ा आगे संगतता और अन्य niceties, और शायद यही है कि मैं इसका उपयोग कर समाप्त करूँगा, लेकिन मुझे यह जानकर उत्सुकता होगी कि मैं धारावाहिक संग्रह के साथ किस तरह के प्रदर्शन प्राप्त कर सकता हूं । और स्पष्ट रूप से, मुझे लगता है कि मैं ऐसा करके बहुत सी चीजें सीख सकता हूं। तो मैं वैकल्पिक समाधान में रूचि रखता हूं;

मैं Cocotron के स्रोतों से प्रेरित किया गया है, एक खुला स्रोत NSArchiver

प्रदान

TLDR: मैं NSArchiver


के पुनर्निर्माण के लिए मैं एआरसी का उपयोग कर रहा है, के लिए संकलन NSCoder उपवर्ग करना चाहते हैं आईओएस 6 & 7, और अब के लिए 32 बिट सिस्टम मानते हैं।

मैं अब के लिए वस्तुओं या तार को संदर्भित में दिलचस्पी नहीं है, मैं सिर्फ उपयोग कर रहा हूँ एक NSHashTable (weakObjectsHashTable) वर्ग के नाम को रोकने के लिए दोहराया जा करने के लिए: कक्षाएं पहली बार उनका सामना कर रहे हैं वर्णित किया जाएगा, और फिर संदर्भ से संबोधित किया जाता ।

मैं एक NSMutableData उपयोग कर रहा हूँ संग्रह का निर्माण करने की:

@interface Archiver { 
    NSMutableData *_data; 
    void *_bytes; 
    size_t _position; 
    NSHashTable *_classes; 
} 
@end 

बुनियादी तरीके हैं:

-(void)_expandBuffer:(NSUInteger)length 
{ 
    [_data increaseLengthBy:length]; 
    _bytes = _data.mutableBytes; 
} 

-(void)_appendBytes:(const void *)data length:(NSUInteger)length 
{ 
    [self _expandBuffer:length]; 
    memcpy(_bytes+_position, data, length); 
    _position += length; 
} 

मैं _appendBytes:length: उपयोग कर रहा हूँ आदिम प्रकार डंप करने के लिए इस तरह के रूप int, char, float, double ... आदि कुछ भी दिलचस्प नहीं है।

सी शैली तार इस उतना ही अरुचिकर विधि का उपयोग कर फेंक दिया जाता है:

-(void)_appendCString:(const char*)cString 
{ 
    NSUInteger length = strlen(cString); 
    [self _appendBytes:cString length:length+1]; 

} 

और अंत में, संग्रह कक्षा की जानकारी और वस्तुओं:

-(void)_appendReference:(id)reference { 
    [self _appendBytes:&reference length:4]; 
} 

-(void)_appendClass:(Class)class 
{ 
    // NSObject class is always represented by nil by convention 
    if (class == [NSObject class]) { 
     [self _appendReference:nil]; 
     return; 
    } 

    // Append reference to class 
    [self _appendReference:class]; 

    // And append class name if this is the first time it is encountered 
    if (![_classes containsObject:class]) 
    { 
     [_classes addObject:class]; 
     [self _appendCString:[NSStringFromClass(class) cStringUsingEncoding:NSASCIIStringEncoding]]; 
    } 
} 

-(void)_appendObject:(const id)object 
{ 
    // References are saved 
    // Although we don't handle relationships between objects *yet* (we could do it the exact same way we do for classes) 
    // at least it is useful to determine whether object was nil or not 
    [self _appendReference:object]; 

    if (object==nil) 
     return; 

    [self _appendClass:[object classForCoder]]; 
    [object encodeWithCoder:self]; 

} 

मेरी वस्तुओं की encodeWithCoder: तरीकों कि सभी तरह दिखते हैं, कुछ भी नहीं फैंसी:

[aCoder encodeValueOfObjCType:@encode(double) at:&_someDoubleMember]; 
[aCoder encodeObject:_someCustomClassInstanceMember]; 
[aCoder encodeObject:_someMutableArrayMember]; 

डिकोडिंग काफी समान तरीके से चला जाता है; अनारक में NSMapTable कक्षाएं हैं जो पहले से ही जानते हैं और कक्षा संदर्भ के नाम की तलाश में हैं जो इसे नहीं जानते हैं।

-(id)_extractReference 
{ 
    id reference; 
    [self _extractBytesTo:&reference length:4]; 
    return reference; 
} 


-(Class)_extractClass 
{ 

    // Lookup class reference 
    id classReference = [self _extractReference]; 

    // NSObject is always nil 
    if (classReference==nil) 
     return [NSObject class]; 

    // Do we already know that one ? 
    if (![_classes objectForKey:classReference]) 
    { 
     // If not, then the name should follow 

     char *classCName = [self _extractCString]; 
     NSString *className = [NSString stringWithCString:classCName encoding:NSASCIIStringEncoding]; 
     free(classCName); 

     Class class = NSClassFromString(className); 

     [_classes setObject:class forKey:classReference]; 
    } 

    return [_classes objectForKey:classReference]; 

} 

-(id)_extractObject 
{ 
    id objectReference = [self _extractReference]; 

    if (!objectReference) 
    { 
     return nil; 
    } 

    Class objectClass = [self _extractClass]; 
    id object = [[objectClass alloc] initWithCoder:self]; 

    return object; 

} 

:

@interface Unarchiver(){ 
    NSData *_data; 
    const void *_bytes; 
    NSMapTable *_classes; 
} 

@end 

मैं

-(void)_extractBytesTo:(void*)data length:(NSUInteger)length 

और

-(char*)_extractCString 

रोचक सामग्री शायद वस्तु डिकोडिंग कोड में है की बारीकियों के साथ आप बोर नहीं होंगे और अंत में, केंद्रीय विधि (मैं डब्ल्यू यदि समस्या यहीं कहीं है आश्चर्य नहीं ould)

-(void)decodeValueOfObjCType:(const char *)type at:(void *)data 
{ 


    switch(*type){ 
     /* snip, snip */ 
     case '@': 
      *(id __strong *) data = [self _extractObject]; 
      break; 
    } 
} 

initWithCoder: विधि encodeWithCoder: के पिछले टुकड़ा करने के लिए इसी है कि

if (self = [super init]) { 
    // Order is important 
    [aDecoder decodeValueOfObjCType:@encode(double) at:& _someDoubleMember]; 
    _someCustomClassInstanceMember = [aDecoder decodeObject]; 
    _someMutableArrayMember = [aDecoder decodeObject]; 
} 
return self; 

मेरे decodeObject कार्यान्वयन की तरह कुछ जाना होगा बिल्कुल _extractObject है।

अब, यह सब अच्छा और अच्छी तरह से काम करना चाहिए। और तथ्य की बात के रूप में; मैं अपनी कुछ वस्तुओं को संग्रहित/अनारक्षित करने में सक्षम हूं। अभिलेखागार ठीक दिखते हैं, इस हद तक कि मैं उन्हें हेक्स संपादक में निरीक्षण करने के लिए तैयार हूं, और मैं double एस वाले किसी अन्य वर्ग के NSMutableArray एस वाले कुछ कस्टम वर्गों को अनारक्षित करने में सक्षम हूं।


लेकिन किसी कारण से, अगर मैं एक की NSNumberNSMutableArray युक्त मेरी वस्तु में से एक संग्रह से निकालने की कोशिश करते हैं, मैं इस समस्या में पड़:

malloc: *** error for object 0xc112cc: pointer being freed was not allocated 

में NSNumber प्रति एक लाइन होने के लिए वहाँ लगता है सरणी, और पता 0xc112cc प्रत्येक पंक्ति के लिए समान है। malloc_error_break में ब्रेकपॉइंट डालने से मुझे पता चलता है कि त्रुटियां -[NSPlaceholderNumber initWithCoder:] से हैं (मेरी _extractObject विधि से बुलाया गया है)।

क्या यह एआरसी के उपयोग से जुड़ी कोई समस्या है? मैं क्या खो रहा हूँ ?

+0

क्या आप एक न्यूनतम initWithCoder साझा कर सकते हैं: कार्यान्वयन? – James

+0

मैंने अपनी पोस्ट को नमूना 'initWithCoder: 'कार्यान्वयन के साथ अपडेट किया। यह काफी सरल है। – Olotiar

+0

में टैग पॉइंटर के साथ कुछ करने के लिए हो सकता है, चेकिंग आपके द्वारा संग्रहीत वास्तविक मूल्य '0xc112cc' है? –

उत्तर

2

मेरी त्रुटि -(void)encodeValueOfObjCType:(const char *)type at:(const void *)addr के दूसरे तर्क की गलतफहमी से जुड़ी हुई थी, जहां type एक सी-स्ट्रिंग (*type == '*') का प्रतिनिधित्व करता है।उस स्थिति में addrconst char ** है, जो const char * पर एक सूचक है, जो स्वयं को 0char की समाप्ति सरणी को इंगित करता है जिसे एन्कोड किया जाना चाहिए।

NSNumber encodeWithCoder: एक छोटे से सी-स्ट्रिंग चर समर्थन के प्रकार के मूल्य का प्रतिनिधित्व (int के लिए एक i, d डबल के लिए, आदि यह @encode निर्देश के बराबर AFAIK है) encodes।

मेरे पिछले अशुद्ध अर्थ एक गलत एन्कोडिंग/डिकोडिंग बनाया (addr संभालने एक const char * था), और initWithCoder: इस प्रकार (असफल रहा था लब्बोलुआब यह है: यह free के एक ढेर चर कोशिश कर रहा था, इस प्रकार त्रुटि संदेश और तथ्य यह है कि पता समारोह के हर कॉल के लिए हमेशा एक ही था)।

अब मेरे पास एक कार्यान्वयन कार्यान्वयन है। यदि कोई दिलचस्पी लेता है, तो कोड एमआईटी लाइसेंस के तहत मेरे GitHub पर है।

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