将复杂对象写入文件objective-c

时间:2012-09-04 12:34:44

标签: objective-c serialization

我发现很难编写/读取自定义对象数组。在我的应用程序中,Contact类有一个NSDictionary作为属性,这个字典有数组作为键的对象。 我使用 NSCoder NSKeyedArchiever 序列化/反序列化我的对象,甚至尝试了 NSPropertyList 序列化。一旦开始序列化NSDictionary,我总是在序列化时遇到错误。这是我的代码,我没有找到关于如何序列化具有复杂结构的自定义对象的一般答案?

//Contact.m
//phoneNumbers is a NSDictionary
#pragma mark Encoding/Decoding
-(void)encodeWithCoder:(NSCoder *)aCoder
{
    NSLog(@"Encoding");
    [aCoder encodeObject:self.firstName forKey:@"firstName"];
    NSLog(@"First name encoded");
    [aCoder encodeObject:self.lastName forKey:@"lastName"];
    NSLog(@"Last name encoded");
    [aCoder encodeInt:self.age forKey:@"age"];
    NSLog(@"Age encoded");

    NSString *errorStr;
    NSData *dataRep = [NSPropertyListSerialization dataFromPropertyList:self.phoneNumbers
                                                                 format:NSPropertyListXMLFormat_v1_0
                                                       errorDescription:&errorStr];
    NSLog(@"Data class %@", [dataRep class]);
    if(!dataRep)
    {
        NSLog(@"Error encoding %@", errorStr);
    }

    [aCoder encodeObject:dataRep forKey:@"phones"];

    NSLog(@"Encoding finished");

}


- (id) initWithCoder: (NSCoder *)coder
{
    if (self = [super init])
    {
        [self setFirstName:[coder decodeObjectForKey:@"firstName"]];
        [self setLastName:[coder decodeObjectForKey:@"lastName"]];

        [self setAge:[coder decodeIntForKey:@"age"]];

        NSString *errorStr;
        NSData *data=[coder decodeObjectForKey:@"phones"];
        NSDictionary *propertyList = [NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListImmutable format:NULL errorDescription:&errorStr];
        if(!propertyList)
        {
            NSLog(@"Error %@", errorStr);
        }

        [self setPhoneNumbers:propertyList];
    }
    return self;
}

//Serializing/Deserializing an array of Contact objects:
#pragma mark Import/Export

//Export Contacts to file
-(void)exportContactsToFile
{
    BOOL done=[NSKeyedArchiver archiveRootObject:self.contacts toFile:[PathUtility getFilePath:@"phonebook"]];
    NSLog(@"Export done: %i", done);
}

//Import Contacts from file

-(void)importContactsFromFile
{

    self.contacts = [NSKeyedUnarchiver unarchiveObjectWithFile:[PathUtility getFilePath:@"phonebook"]];
}

是否有一种通用的好方法来序列化/反序列化objective-c中的对象?谢谢 我得到的错误是: 0objc_msgSend 1 CF_Retain ... 这是堆栈跟踪,但我没有其他错误(

2 个答案:

答案 0 :(得分:3)

您不应该将NSPropertyListSerialization用于self.phoneNumbers。 NSDictionary遵守NSCoding协议。

所以,[aCoder encodeObject:self.phoneNumbers forKey:@"phones"];就足够了。

只要一个类遵循NSCoding(几乎所有Apple提供的类都这样做),你就可以使用-encodeObject:forKey:,因为该方法将调用该对象的-encodeWithCoder:

的实现

答案 1 :(得分:3)

我的专有库中有一个特殊的类,它自动读取其属性列表,并使用getter和setter对对象进行编码和解码。对不起,我不能在这里分享代码,但我至少可以一步一步地为您提供课程的工作方式:

  1. 首先,该类必须实现NSCodingNSCopying协议。

  2. + (void)initialize内,使用class_copyPropertyList()property_getName()property_copyAttributeList()迭代通过类属性的定义。有关这些功能的详细信息,请参阅Objective-C Runtime Programming Guide

  3. 对于每个属性,运行其属性列表并获取带有strncmp(attribute.name, "T", 1) == 0的属性(是的,它是一个c字符串)。使用该属性值来确定属性的类型。例如,“i”表示int,“I”表示unsigned int,如果它以“{”开头,那么它是struct等。请参阅this page on the Type Encoding

  4. 将属性名称类型对存储在NSDictionary中。在属性迭代结束时,使用类名作为键将此字典存储在静态和全局NSMutableDictionary中。

  5. 要支持自动编码,请实现- (void)encodeWithCoder:(NSCoder *)aCoder以通过属性名称类型对进行迭代,调用属性getter方法(通常为- (returnType)propertyName)并使用适当的{在编码器内对其进行编码{1}}方法(例如encodeType:encodeInt:encodeFloat:encodeObject:等。

  6. 要支持自动解码,请实施encodeCGPoint:以通过属性名称类型对进行迭代,使用适当的- (id)initWithCoder:(NSCoder *)aDecoder方法(例如decodeTypeForKey:方法)从解码器对其进行解码(decodeIntForKey:,{ {1}},decodeFloatForKey:decodeObjectForKey:等)。并调用属性setter方法(通常为decodeCGPointForKey:)。

  7. 实现一个触发编码的实例方法(幸运的是我可以在这里分享这个方法^ __ ^):

    - (void)setPropertyName:
  8. 获得NSMutableData *data = [NSMutableData data]; NSKeyedArchiver *arc = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; [arc encodeRootObject:self]; [arc finishEncoding]; [arc release]; return data; 后,您可以使用NSData甚至writeToFile:atomically:

  9. 此外,实现一个类方法,该方法返回从文件加载的对象的新实例:

    [[NSUserDefaults standardUserDefaults] setObject:[self wrapInNSData] forKey:key]
  10. 最后,为了使另一个对象类支持这种自动编码解码,该类需要扩展特殊类。

  11. 对不起,这有点长,但对于我的情况,我创建这个课程的额外麻烦确实节省了很多时间。今天奋斗,明天轻松;)