【问题标题】:NSKeyedUnarchiver finds corrupted dataNSKeyedUnarchiver 发现损坏的数据
【发布时间】:2017-11-12 02:14:02
【问题描述】:

我有一个归档程序和一个取消归档程序可以协同工作。

第二个程序的运行结果表明,虽然两个对象(myFoo1)中的一个已经很好地归档、存储和稍后取消归档,但另一个(myBook)似乎无法正确取消归档。我只是不知道在归档和/或取消归档过程中会发生什么,这些过程似乎损坏了原始数据的一部分。我已经仔细确保编码密钥与解码密钥相同,但程序仍然存在。

归档程序的代码:

#import <Foundation/Foundation.h>

@interface Foo : NSObject <NSCoding>

@property (copy, nonatomic) NSString * strVal;
@property int intVal;
@property float floatVal;

-(void) display;

@end

#import "Foo.h"

@implementation Foo

@synthesize  strVal, intVal, floatVal;

-(void) encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject: strVal forKey: @"FoostrVal"];
    [aCoder encodeInt: intVal forKey: @"FoointVal"];
    [aCoder encodeFloat: floatVal forKey: @"FoofloatVal"];
    // NSLog (@"Foo encodeWithCoder called"); called
}

-(id) initWithCoder:(NSCoder *)aDecoder
{
    strVal = [aDecoder decodeObjectForKey: @"FoostrVal"];
    intVal = [aDecoder decodeIntForKey: @"FoointVal"];
    floatVal = [aDecoder decodeFloatForKey: @"FoofloatVal"];
    // NSLog (@"Foo initWithCoder called"); not called
    return self;
}

-(void) display
{
    NSLog (@"%@ %i %g", strVal, intVal, floatVal);
}

@end

#import <Foundation/Foundation.h>

@interface AddressCard : NSObject <NSCoding>

@property (nonatomic, copy) NSString * name, * email;

-(instancetype) initWithName: (NSString *) theName andEmail: (NSString *) theEmail;
-(void) showCard;

@end

#import "AddressCard.h"

@implementation AddressCard

@synthesize name, email;

-(instancetype) initWithName: (NSString *) theName andEmail: (NSString *) theEmail
{
    self = [super init];
    if (self) {
        self.name = [NSString stringWithString: theName];
        self.email = [NSString stringWithString: theEmail];
    }
    return self;
}

-(instancetype) init
{
    return [self initWithName: @"NoName" andEmail: @"NoEmail"];
}

-(void) encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject: name forKey: @"AddrCardName"];
    [aCoder encodeObject: email forKey: @"AddrCardEmail"];
    // NSLog (@"AddressCard encodeWithCoder called"); called
}

-(id) initWithCoder:(NSCoder *)aDecoder
{
    [aDecoder decodeObjectForKey: @"AddrCardName"];
    [aDecoder decodeObjectForKey: @"AddrCardEmail"];
    // NSLog (@"AddressCard initWithCoder called"); not called

    return self;
}

-(void) showCard
{
    NSLog (@"%-20s%-40s", [name UTF8String], [email UTF8String]);
}

@end

#import "AddressCard.h"

@interface AddressBook : NSObject <NSCoding>

@property (nonatomic, copy) NSString * title;
@property (nonatomic, strong) NSMutableArray * book;

-(instancetype) initWithTitle: (NSString *) title;
-(void) addCard: (AddressCard *) card;
-(void) list;

@end

#import "AddressBook.h"

@implementation AddressBook

@synthesize title, book;

-(instancetype) initWithTitle: (NSString *) theTitle
{
    self = [super init];
    if (self) {
        title = [NSString stringWithString: theTitle];
        book = [NSMutableArray array];
    }
    return self;
}

-(instancetype) init
{
    return [self initWithTitle: @"NoTitle"];
}

-(void) encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject: title forKey: @"AddrBookTitle"];
    [aCoder encodeObject: book forKey: @"AddrBookBook"];
    // NSLog (@"AddressBook encodeWithCoder called"); called
}

-(id) initWithCoder:(NSCoder *)aDecoder
{
    [aDecoder decodeObjectForKey: @"AddrBookTitle"];
    [aDecoder decodeObjectForKey: @"AddrBookBook"];
    // NSLog (@"AddressBook initWithCoder called"); not called

    return self;
}

-(void) addCard: (AddressCard *) card
{
    if ([book containsObject: card] == YES)
        NSLog (@"The card already exists in %@", title);
    else
        [book addObject: card];
}

-(void) list
{
    for (AddressCard * card in book)
        [card showCard];
}

@end

#import "AddressBook.h"
#import "Foo.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Foo * myFoo1 = [[Foo alloc] init];
        NSMutableData * dataArea;
        NSKeyedArchiver * archiver;
        AddressBook * myBook = [[AddressBook alloc] initWithTitle: @"Steve's Address Book"];

        NSString * aName = @"Julia Kochan";
        NSString * aEmail = @"jewls337@axlc.com";
        NSString * bName = @"Tony Iannino";
        NSString * bEmail = @"tony.iannino@tecfitness.com";
        NSString * cName = @"Stephen Kochan";
        NSString * cEmail = @"steve@steve_kochan.com";
        NSString * dName = @"Jamie Baker";
        NSString * dEmail = @"jbaker@hitmail.com";
        NSString * filePath = @"addrbook.arch";

        AddressCard * card1 = [[AddressCard alloc] initWithName: aName andEmail: aEmail];
        AddressCard * card2 = [[AddressCard alloc] initWithName: bName andEmail: bEmail];
        AddressCard * card3 = [[AddressCard alloc] initWithName: cName andEmail: cEmail];
        AddressCard * card4 = [[AddressCard alloc] initWithName: dName andEmail: dEmail];

        // Add some cards to the address book
        [myBook addCard: card1];
        [myBook addCard: card2];
        [myBook addCard: card3];
        [myBook addCard: card4];

        myFoo1.strVal = @"This is the string";
        myFoo1.intVal = 12345;
        myFoo1.floatVal = 99.8;

        // Set up a data area and connect it to an NSKeyedArchiver object
        dataArea = [NSMutableData data];
        archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData: dataArea];

        // Now we can begin to archive objects
        [archiver encodeObject: myBook forKey: @"myaddrbook"];
        [archiver encodeObject: myFoo1 forKey: @"myfoo1"];
        [archiver finishEncoding];

        // Write the archived data area to a file
        if ([dataArea writeToFile: filePath atomically: YES] == NO)
            NSLog (@"Archiving failed!");
        NSLog (@"All operations successful!");

    }
    return 0;
}

解档程序的代码:

#import <Foundation/Foundation.h>

@interface Foo : NSObject <NSCoding>

@property (copy, nonatomic) NSString * strVal;
@property int intVal;
@property float floatVal;

-(void) display;

@end

#import "Foo.h"

@implementation Foo

@synthesize  strVal, intVal, floatVal;

-(void) encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject: strVal forKey: @"FoostrVal"];
    [aCoder encodeInt: intVal forKey: @"FoointVal"];
    [aCoder encodeFloat: floatVal forKey: @"FoofloatVal"];
    // NSLog (@"Foo encodeWithCoder called"); not called
}

-(id) initWithCoder:(NSCoder *)aDecoder
{
    strVal = [aDecoder decodeObjectForKey: @"FoostrVal"];
    intVal = [aDecoder decodeIntForKey: @"FoointVal"];
    floatVal = [aDecoder decodeFloatForKey: @"FoofloatVal"];
    // NSLog (@"Foo initWithCoder called"); called

    return self;
}

-(void) display
{
    NSLog (@"%@\n%i\n%g", strVal, intVal, floatVal);
}

@end

#import <Foundation/Foundation.h>

@interface AddressCard : NSObject <NSCoding>

@property (nonatomic, copy) NSString * name, * email;

-(instancetype) initWithName: (NSString *) theName andEmail: (NSString *) theEmail;
-(void) showCard;

@end

#import "AddressCard.h"

@implementation AddressCard

@synthesize name, email;

-(instancetype) initWithName: (NSString *) theName andEmail: (NSString *) theEmail
{
    self = [super init];
    if (self) {
        self.name = [NSString stringWithString: theName];
        self.email = [NSString stringWithString: theEmail];
    }
    return self;
}

-(instancetype) init
{
    return [self initWithName: @"NoName" andEmail: @"NoEmail"];
}

-(void) encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject: name forKey: @"AddrCardName"];
    [aCoder encodeObject: email forKey: @"AddrCardEmail"];
    // NSLog (@"AddressCard encodeWithCoder called"); not called
}

-(id) initWithCoder:(NSCoder *)aDecoder
{
    [aDecoder decodeObjectForKey: @"AddrCardName"];
    [aDecoder decodeObjectForKey: @"AddrCardEmail"];
    // NSLog (@"AddressCard initWithCoder called"); called

    return self;
}

-(void) showCard
{
    NSLog (@"%-20s%-40s", [name UTF8String], [email UTF8String]);
}

@end

#import "AddressCard.h"

@interface AddressBook : NSObject <NSCoding>

@property (nonatomic, copy) NSString * title;
@property (nonatomic, strong) NSMutableArray * book;

-(instancetype) initWithTitle: (NSString *) title;
-(void) addCard: (AddressCard *) card;
-(void) list;

@end

#import "AddressBook.h"

@implementation AddressBook

@synthesize title, book;

-(instancetype) initWithTitle: (NSString *) theTitle
{
    self = [super init];
    if (self) {
        title = [NSString stringWithString: theTitle];
        book = [NSMutableArray array];
    }
    return self;
}

-(void) encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject: title forKey: @"AddrBookTitle"];
    [aCoder encodeObject: book forKey: @"AddrBookBook"];
    // NSLog (@"AddressBook encodeWithCoder called"); not called

}

-(id) initWithCoder:(NSCoder *)aDecoder
{
    [aDecoder decodeObjectForKey: @"AddrBookTitle"];
    [aDecoder decodeObjectForKey: @"AddrBookBook"];
    // NSLog (@"AddressBook initWithCoder called"); called

    return self;
}

-(void) addCard: (AddressCard *) card
{
    if ([book containsObject: card] == YES)
        NSLog (@"The card already exists in %@", title);
    else
        [book addObject: card];
}

-(void) list
{
    for (AddressCard * card in book)
        [card showCard];
}

@end

#import "AddressBook.h"
#import "Foo.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSData * dataArea;
        NSKeyedUnarchiver * unarchiver;
        Foo * myFoo1;
        AddressBook * myBook;
        NSString * filePath = @"addrbook.arch";

        // Read in the archive and connect an
        // NSkeyedUnarchiver object to it

        dataArea = [NSData dataWithContentsOfFile: filePath];
        if (!dataArea) {
            NSLog (@"Can't read back archive file!");
            return 1;
        }
        unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData: dataArea];
        // Decode the objects we previously stored in the archive
        myBook = [unarchiver decodeObjectForKey: @"myaddrbook"];
        myFoo1 = [unarchiver decodeObjectForKey: @"myfoo1"];
        [unarchiver finishDecoding];

        // Verify that the resote was successful
        if (myBook != nil) {
            if ([myBook.book count] == 0)
                NSLog (@"Data corrupted in myBook!");
            else
                [myBook list];
        }
        else
            NSLog (@"Load myBook failed!");

        if (myFoo1 != nil)
            [myFoo1 display];
        else
            NSLog (@"Load myFoo1 failed!");
    }
    return 0;
}

上述解压程序的运行结果: myBook 中的数据已损坏! 这是字符串 12345 99.8 程序以退出代码结束:0

【问题讨论】:

  • 有错误日志吗?可以展示一下吗?
  • Xcode 没有抛出任何错误信息。如果有错误日志,我在哪里可以检索到错误日志?谢谢。
  • 你能给我 Xcode 在应用停止时的屏幕截图吗?
  • 我制作了 Xcode 屏幕的屏幕截图,但我不知道如何将其提供给您。谢谢。
  • 是的,我对 NSKeyedArchiver 和 NSKeyedUnarchiver 的工作方式还不是很熟悉,而且我很粗心。我很感谢在 Stack Overflow 上像你一样乐于助人的朋友。我觉得真的很幸运。希望以后能在这里得到更多好心人的帮助。

标签: objective-c cocoa nskeyedarchiver nskeyedunarchiver


【解决方案1】:

这里有两个问题:你的 AddressBookAddressCard -initWithCoder: 都没有实现

  1. 致电self = [super init]。这不是问题的根本原因,但肯定是不正确的
  2. 也不将解码结果分配给您的任何属性。你解码AddrBookBook,但从不将它分配给self.book;因此,myBook.book == nil,以及[myBook.book count] == 0。所有的解码调用都应该是myIvar = [aDecoder decodeObjectForKey:...]

【讨论】:

  • 非常感谢!我可以理解您解决方案的第 2 点。但是我仍然无法理解第 1 点。教授Objective-C编程的书说,诸如“self = [super init]”之类的语句应该在子类化的属性初始化方法中。书上说的有什么问题吗?再次感谢。
  • 你指定的初始化器——和-initWithCoder:可以被认为是一个——总是必须调用超类的指定初始化器。 (顺便说一句:辅助初始化器也必须调用它自己的类的指定初始化器,所以唯一不调用另一个初始化器的初始化器在根类中,即NSObject。)如果超类采用,则调用-initWithCoder:编码协议,或者,如果不是,它具有的其他指定初始化程序之一。除非您编写一个新的根类(非常不可能),否则您的-init...-实现中总是有一个init...-call。没有例外。
  • 感谢您的解释,阿明。现在我可以理解 Itai 的第一点了。我从没想过 -initWithCoder: 是指定的初始化程序,因为 -initWith... 是。
猜你喜欢
  • 2011-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-21
  • 2023-04-06
  • 1970-01-01
  • 2010-12-25
相关资源
最近更新 更多