iOS因未捕获的异常“NSUnknownKeyException”终止应用程序。

3

我对Objective-C和iOS编程还比较陌生,遇到了一个奇怪的错误。所涉及的应用程序使用一个自定义类型的NSMutableArray进行初始化,该类型是使用NSObject创建的。应用程序可操作此数组。如果在应用程序运行期间添加新值,则使用NSUserDefaults保存这些值,并在下次打开应用程序时从NSUserDefaults中获取这些值以及默认值。

以下是出现的错误:

Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFConstantString 0xb404> valueForUndefinedKey:]: this class is not key value coding-compliant for the key score.'
*** First throw call stack:
(0x1c9b012 0x10d8e7e 0x1d23fb1 0xb84d1d 0xaf100b 0xaf0fbd 0xb0f247 0xb3023c 0xb30056 0x3e40 0x3c5f 0x11f5ad 0x10ec705 0x202c0 0x20258 0x242ff4 0x10ec705 0x202c0 0x20258 0xe1021 0xe157f 0xe1056 0x246af9 0x10ec705 0x202c0 0x20258 0xe1021 0xe157f 0xe06e8 0x4fcef 0x4ff02 0x2dd4a 0x1f698 0x1bf6df9 0x1bf6ad0 0x1c10bf5 0x1c10962 0x1c41bb6 0x1c40f44 0x1c40e1b 0x1bf57e3 0x1bf5668 0x1cffc 0x290d 0x2835)
libc++abi.dylib: terminate called throwing an exception

我不确定错误是什么,也不知道如何进行调试。
之前这段代码运行得非常顺利,我的做法只是从默认预设元素列表中删除了一两个元素,然后在模拟器中模拟删除应用程序并重新编译代码。自那以后,我的程序就会崩溃,并出现以上所述的错误信息,我不知道该如何修复它。
如果有人能帮助我解决这个问题,那将是非常棒的。我可以根据需要附上代码,但我不确定哪些代码是相关的,而且可能太多了,无法在项目中全部发布。
以下是编码和解码我自定义名称NSObject类名为name.h属性的代码:
-(void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeObject:self.name forKey:@"name"];
    [encoder encodeInteger:self.score forKey:@"score"];

}

-(id)initWithCoder:(NSCoder *)decoder
{
    if((self = [super init]))
    {
        self.name = [decoder decodeObjectForKey:@"name"];
        self.score = [decoder decodeIntegerForKey:@"score"];
    }

    return self;
}

从类中检索数据,如果这很重要,那么这段代码出现在appdelegate.m中:

NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"dataArray"];


NSInteger score = 0;


NSMutableArray *temp = [[NSMutableArray alloc] initWithObjects:@"name", nil];

NSMutableArray *tempList = [[NSMutableArray alloc] init];

for(NSString *y in temp)
{
    Name *name = [[Name alloc] init];
    name.name  = y;
    name.score = score;
    [tempList addObject:name];


}

if (data)
{

    NSArray *list = [NSKeyedUnarchiver unarchiveObjectWithData:data];
   NSMutableArray *names = [[NSMutableArray alloc]initWithArray:list];
  // [_nameList addObjectsFromArray:temp];
    NSMutableArray *t = [[names arrayByAddingObjectsFromArray:tempList ] mutableCopy];

    _nameList = [[NSMutableArray alloc]init];

    [_nameList addObjectsFromArray:t];


}
else 
{
    //First time load or data is not saved yet
    _nameList = [[NSMutableArray alloc] initWithObjects:@"name", nil];

}

在关闭时保存数组:

- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
NSData *data =[NSKeyedArchiver archivedDataWithRootObject:_nameList];


[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"dataArray"];
}

同样的代码也在applicationDidEnterBackground中。

按'score'排序的代码。

- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:    (UIViewController *)viewController
{


    if(viewController == _viewController3)
    {
        [self sortNames:_nameList];
        [[(ThirdViewController*)_viewController3 topList] reloadData];

    }
}

-(void)sortNames:(NSMutableArray*)test
{



NSArray* temp = [[NSArray alloc] initWithArray:test];


NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"score" ascending:NO];
NSArray *sortedLinks = [[temp sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]] mutableCopy];

_nameList = (NSMutableArray*) sortedLinks;
}

1
你正在给一个不符合键值编码规范的对象分配一个键。你能否发布一些代码,展示“score”键是什么以及你如何使用它? - jakenberg
需要更多信息才能帮助您。请发布代码以获得更具体的帮助。问题太模糊了。要进行调试,您可以在使用NSUserDefaults的代码附近放置断点,然后重新运行代码。关于如何调试的好入门视频:http://www.youtube.com/watch?v=RKqDfYVSjg8 - lakshmen
在 @jsksma2 中添加了一些代码。 - PhoenixLament
更好的做法可能是设置异常断点。 - jakenberg
1
你能否添加代码,用于创建、编码和解码对象的部分? - Tobi
@Tobi 在 applicationWillTerminate 和 applicationDidEnterBackground 中添加了我用来保存到 NSUserDefaults 的代码。 - PhoenixLament
2个回答

2

我来发表一下自己的意见。你有这样一行代码:

[encoder encodeObject:self.name forKey:@"name"];

然后是这一行:

Name *name = [[Name alloc] init];

这让我想到 "self.name" 属性是这些 "Name" 自定义子类之一。如果您创建了自定义子类并希望进行编码,则需要将 "encodewithcoder" 方法添加到自定义子类中,并将其作为原始值对其所有属性和实例变量进行编码。也就是说,您的 Name 类需要有自己的 encodewithcoder 方法,以编码已存储为工厂对象或c原语的所有属性和实例变量。编辑:我还很新,非常重视我的声誉。如果我错了,请评论并删除,但请不要将我投票到无人问津。

0

这里:

_nameList = [[NSMutableArray alloc] initWithObjects:@"name", nil];

你正在将一个NSString添加到_nameList数组中。稍后,你保存了该数组。下次加载该数组时,其中有NSString@"name"。我猜想在某个时刻,你会遍历_nameList中的项目并尝试获取或设置score,由于你在NSString的子类上调用此方法,因此你会得到NSUnknownKeyException

我认为你想要的是将上面的行替换为以下内容:(假设你上面提到的name.h文件中的类名为Name

Name *newName = [[Name alloc] init]; //or initialize the way you need to
_nameList = [[NSMutableArray alloc] initWithObjects:newName, nil];

实际上,在下面几行中完成了此操作:NSMutableArray *tempList = [[NSMutableArray alloc] init];for(NSString *y in temp) { Name *name = [[Name alloc] init]; name.name = y; name.score = score; [tempList addObject:name];} - PhoenixLament
你将其分配回 _nameList 吗? - Tobi
如果(data) {NSArray *list = [NSKeyedUnarchiver unarchiveObjectWithData:data]; NSMutableArray *names = [[NSMutableArray alloc]initWithArray:list]; // [_nameList addObjectsFromArray:temp]; NSMutableArray *t = [[names arrayByAddingObjectsFromArray:tempList ] mutableCopy];_nameList = [[NSMutableArray alloc]init]; [_nameList addObjectsFromArray:t];} 否则 { //第一次加载或数据尚未保存 _nameList = [[NSMutableArray alloc] initWithObjects:@"名称", nil];} - PhoenixLament
1
嗯,这肯定与它有关,因为错误信息明确指出,你正在尝试在字符串上调用“score”。作为一种蛮力尝试,你可以搜索整个项目中的“score”,在每个出现的地方设置断点,以找出异常发生的位置。然后,你可以检查接收该消息的对象,并希望能够回溯问题的根源。 - Tobi
我发现崩溃是在切换视图控制器时发生的。在appdelegate.m中,我有一个函数来检查是否在第三个视图控制器上,通过分数对namelist的nsmutablearray进行排序,并打印到tableview上。似乎在NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"score" ascending:NO]; NSArray *sortedLinks = [[temp sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]] mutableCopy];处出现了问题。 - PhoenixLament
在这一行 NSArray *sortedLinks = [[temp sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]] mutableCopy]; 中设置一个断点,当程序执行到此处停止时,在控制台中输入 po temp 并将输出内容发布在此处。 - Tobi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接