施法后的Objective-C类类型不匹配

时间:2012-10-25 15:49:03

标签: iphone objective-c ios json

使用NSJSONSerialization解析来自Web服务的JSON响应后,我使用+isKindOfClass:来确保服务器返回了我期望的数据类型。使用这种方法,我遇到了一些奇怪的行为,我将用一个例子说明。

考虑以下对象:

// Definitions
NSDictionary *son = @{ @"firstname" : @"James", @"lastname" : @"Appleseed" };
NSDictionary *daughter = @{ @"firstname" : @"Susan", @"lastname" : @"Appleseed"};
NSArray *children = @[son, daughter];
NSDictionary *father = @{ @"firstname" : @"John", @"lastname" : @"Appleseed" };
NSDictionary *family = @{@"children" : children, @"father" : father};
NSDictionary *pedigree = @{@"family" : family };

这些对象表示从服务器返回的反序列化JSON。现在,如果我想使用子数组来计算使用NSArray -count的子项数,我需要确保子对象是NSArray。例如,如果子对象碰巧是一个字符串,而应用程序需要一个数组,它会崩溃,因为字符串没有实现count方法。请考虑以下代码序列,它实现了所描述的检查:

// First test
id _family = [pedigree objectForKey:@"family"];
if ([_family isKindOfClass:[NSDictionary class]])
{
    NSDictionary *_family = (NSDictionary *)_family;
    id _children = [_family objectForKey:@"children"];

    NSLog(@"Children: %@", _children);
    NSLog(@"Children classname: %@", NSStringFromClass(children.class));

    if ([_children isKindOfClass:[NSArray class]]) {
        NSLog(@"Children is an NSArray");
    } else {
        NSLog(@"Children is not an NSArray");
    }
} else {
    NSLog(@"Family is not an NSDictionary");
}

运行此代码后,控制台输出以下内容:

Children: (null)
Children classname: __NSArrayI
Children is not an NSArray

控制台输出似乎非常显着甚至是矛盾的。当孩子的名字是__NSArrayI时,孩子怎么可能是NSArray?

经过一些调试后,我发现有两种方法可以解决这个问题:

  1. 删除此行NSDictionary *_family = (NSDictionary *)_family;
  2. 使用与_family不同的名称作为转换变量
  3. 如何解释这种行为?

2 个答案:

答案 0 :(得分:2)

在第

NSDictionary *_family = (NSDictionary *)_family;

在当前范围中定义新变量_family,这使得外部变量_family不可见。如果使用ARC进行编译,则Objective-C指针初始化为nil

输出并不矛盾,因为你打印

NSStringFromClass(children.class);

这是children的类(没有下划线),这是一个数组。但_children(带下划线)为nil,因为如上所述,_family为零。

实际上,如果您需要字典,则不需要进行类型转换。你可以做到

NSDictionary *_family = [pedigree objectForKey:@"family"];
if ([_family isKindOfClass:[NSDictionary class]])
{
    NSArray *_children = [_family objectForKey:@"children"];

    if ([_children isKindOfClass:[NSArray class]]) {
        NSLog(@"Children is an NSArray");
    } else {
        NSLog(@"Children is not an NSArray");
    }
} else {
    NSLog(@"Family is not an NSDictionary");
}

答案 1 :(得分:0)

您的变量children包含一个数组,您的变量_childrennil

这解释了打印NSLog的第一个(null)(因为您记录_children,而不是children),而是打印{{1}的第二个NSLog (因为你打印了__NSArrayI对象的类,而不是nil children变量的类。

您的第三个_children表示它不是数组,因为您检查NSLog[_children isKindOfClass:...]_children,因此nil会返回[nil isKindOfClass:...] }。


因此,要解决您的问题,您必须弄清楚NO变量为_children的原因(而nil变量不是)。

这显然是因为您使用隐藏其父级的children变量。因此,行_family显然使用内部NSDictionary *_family = (NSDictionary *)_family;变量,并且与您编写_family的行为完全相同:因为此NSDictionary *foo = (NSDictionary *)foo;foo,所以无论你想要什么,它仍然会成为nil

为内部nil变量使用其他名称,以避免第二个_family变量隐藏外部_family变量,您的问题就会消失。或者更好,完全删除此行,因为在_family上调用方法而不进行强制转换是没有问题的(实际上是id的用途)