【问题标题】:NSData to NSArray error in iPhoneiPhone中的NSData到NSArray错误
【发布时间】:2011-12-14 10:58:12
【问题描述】:

我正在使用 NSURLConnection 从服务器获取 XML 数据。我正在解析数据并将其显示在表格视图中。这一切都按预期工作。现在,我想保存下载的数据以供离线使用。想法是获取下载的 NSData,将其转换为 NSArray 并将其存储到 NSUserDefaults 或单独的文件中。但是,我在将 NSData 转换为 NSArray 时遇到问题。

我将逻辑添加到(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 方法。我正在尝试做的事情如下:

NSError *error;
NSPropertyListFormat plistFormat;
id object = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListImmutable format:&plistFormat error:&error];
if (error != nil) {
   NSLog(@"Error %@", [error localizedDescription]);
   [error release];
}
if ([object isKindOfClass:[NSArray class]]) {
     NSLog(@"IS Array");
     NSArray *objectArray = object;
     [[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver  archivedDataWithRootObject:objectArray] forKey:@"myKey"];
} else {
     NSLog(@"Not an array");
}

在日志中我得到如下:

错误 操作无法完成。 (可可错误 3840。)

不是 数组

如果我删除错误处理并只留下一行

NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:data];

我的应用程序崩溃并显示以下消息:`

由于未捕获的异常而终止应用程序 'NSInvalidArgumentException',原因:'*** -[NSKeyedUnarchiver initForReadingWithData:]: 无法理解的存档 (0x3c, 0x3f, 0x78, 0x6d, 0x6c, 0x20, 0x76, 0x65)'

为什么会这样?什么是 Cocoa 错误 3840?

我的对象实现了 NSCoding 协议,并且有方法 encodeWithCoderinitWithCoder。我的对象的每个属性都必须编码/解码吗?

编辑:这是我的对象:
货币.h

@interface Currency : NSObject<NSCoding>{
    CGFloat value;
        NSString *code;
    NSDate *date;
    NSString *description;
    NSString *imagePath;
}
@property (nonatomic, assign) CGFloat value;
@property (nonatomic, retain) NSString *code;
@property (nonatomic, retain) NSDate *date;
@property (nonatomic, retain) NSString *description;
@property (nonatomic, retain) NSString *imagePath;

货币.m

@implementation Currency

@synthesize value;
@synthesize code;
@synthesize date;
@synthesize description;
@synthesize imagePath;

static NSString * const keyCode = @"code";
static NSString * const keyDescription = @"description";
static NSString * const keyValue = @"value";

- (void)dealloc {
    [code release];
    [date release];
    [description release];
    [imagePath release];
    [super dealloc];
}

- (void)encodeWithCoder:(NSCoder *)coder
{
    if ([coder allowsKeyedCoding]) {
       [coder encodeObject:code forKey: keyCode];
           [coder encodeObject:description forKey: keyDescription];
           [coder encodeFloat:value forKey: keyValue];
        }
}

}

- (id)initWithCoder:(NSCoder *) coder {
    self = [[Currency alloc] init];
    if (self != nil)
    {
        code = [[coder decodeObjectForKey:keyCode] retain];
        description = [[coder decodeObjectForKey:keyDescription] retain];
        value = [coder decodeFloatForKey:keyValue];
    }   
    return self;
}

@end

【问题讨论】:

  • 数据定义是什么?发布 NSCoding 实现的实现。

标签: iphone objective-c ios


【解决方案1】:

修改后的答案:

因此,这里有三个不同的概念:通用 XML、propertyLists(二进制或 XML 格式)和存档。您使用 NSXMLParser 或其他库解析 XML 以转换为 Obj-C 对象。您可以将核心 Obj-C 对象(不包括您的自定义类 Currency)保存到属性列表中并读回它们。或者您可以将任何对象/对象图(使用编码器)存档到存档中,然后使用 NSKeyedUnarchiver 将其读回。

即使它们在下面可能共享实现,但您不能混合使用它们。例如,首先您尝试将 XML 读取为 propertyList,即使 pList 是 XML,您的通用 CurrencyData XML 也没有正确的 plist 格式(例如 no )。此外,即使您要写出 plist,也必须将 Currency 转换为 NSDictionary 才能将其存储在 plist 中,这意味着您不需要编码器。

然后您尝试使用 NSKeyedUnarchiver 读取 XML 并收到消息“无法理解的存档”。也正确,因为它不是用 NSKeyedArchive 创建的。

因此,您不能使用原始流直接 plist 或取消归档到您的对象中。但是要保存解析的 XMLArray 以供离线使用,您可以将 Currency 转换为 NSDictionary 并使用属性列表,或者保持原样并使用 NSKeyedArchive 和 Unarchive 像这样(利用您提供的编码器/解码器):

//To save
NSData * data = [NSKeyedArchiver archivedDataWithRootObject:xmlArray];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"myKey"]; //or store in another file

//To later read
NSData * data2 = [[NSUserDefaults standardUserDefaults] valueForKey:@"myKey"];
NSArray * newXMLArray = [NSKeyedUnarchiver unarchiveObjectWithData:data2];

希望能解决这个问题。调查它也澄清了我自己对何时使用哪种技术的理解。

//原评论: 如果您已经将传入数据解析为 tableView 的模型,为什么不序列化该数据副本而不是原始流?

【讨论】:

  • 这就是我最终要做的。我仍然想知道为什么上面的代码不起作用。
  • 不知道,但有一些可能的路径:首先,我没有你的 XML,但我猜它是以 NSDictionary 的形式出现的,而不是 NSArray。 NSLog(@"data class: %@",[[data class] description]) 来检查。其次,您可以从错误中获得更多有用的信息。请参阅此处的第二个答案:stackoverflow.com/questions/1283960/… 第三,您可能混淆了 XML 和 keyed-archives。 NSKeyedUnarchive,AFAIK,只能读取由 NSKeyedArchive 写入的数据,不能读取任意 XML。最后,我不认为你需要一个只有基本类的对象的编码器。
  • 我似乎也不是 NSDictionary ......无论如何,谢谢你的帮助。我得到的错误是: NSDebugDescription = "在第 2 行遇到未知标签货币"; kCFPropertyListOldStyleParsingError = "Error Domain=NSCocoaErrorDomain Code=3840 \"操作无法完成。 (Cocoa 错误 3840。)\"(第 1 行的格式错误的数据字节组;无效的十六进制)UserInfo=0x625bc70 {NSDebugDescription=第 1 行的格式错误的数据字节组;无效的十六进制}";
  • 你试过 NSLog(@"data class: %@",[[data class] description]) 吗?看起来你得到了很好的进一步信息:“第 2 行上的未知标签货币”(不要担心 kCFProp ......它会再次尝试非 XML 旧式 plist 格式,所以它应该会失败)。 XML 的开头是什么样的?
  • 这是我的 XML 的开头:3.6732
【解决方案2】:

您是否有理由不直接将 NSData 对象保存到文件中

data writeToFile:(NSString*)path atomically:(BOOL)flag

?

我认为您的 NSPropertyList 调用不会出错 - 如果发生错误,propertyListWithData 函数会返回 nil,而我假设错误仅在那时有效。您可能应该检查返回值!= nil,然后打印错误。错误 3840 标志着属性列表错误范围的开始,这本身看起来并不像错误。

propertyListWithData 的描述也说它返回一个属性列表,而不是一个数组,所以 isKindOfClass 说它不是一个数组也就不足为奇了……看这里:

http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSPropertyListSerialization_Class/Reference/Reference.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-07
    • 1970-01-01
    相关资源
    最近更新 更多