0

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 にならないのでしょうか?

少しデバッグした後、この問題を解決するには 2 つの方法があることがわかりました。

  1. この行を削除NSDictionary *_family = (NSDictionary *)_family;
  2. _familyキャストされた変数とは異なる名前を使用する

この動作はどのように説明できますか?

4

2 に答える 2

2

ラインで

NSDictionary *_family = (NSDictionary *)_family;

現在のスコープで新しい変数を定義する_familyと、外側の変数が_family見えなくなります。nilARC でコンパイルすると、Objective-C ポインタが初期化されます。

出力は矛盾しません。なぜなら、あなたが印刷するからです

NSStringFromClass(children.class);

childrenこれは、配列である (アンダースコアなし)のクラスです。しかし、_children(アンダースコア付き)は、上で説明したように nilである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");
}
于 2012-10-25T16:00:14.030 に答える
0

変数childrenには配列が含まれています。変数_childrennilです。

これは、最初NSLogに出力されるもの(ではなく(null)をログに記録するため) を説明しますが、2 番目に出力されるもの( nil変数のクラスではなく、オブジェクトのクラスを出力するため) を説明します。_childrenchildrenNSLog__NSArrayIchildren_children

あなたの 3 番目は、とNSLogをチェックするため、配列ではないと言っているので、 を返します。[_children isKindOfClass:...]_childrennil[nil isKindOfClass:...]NO


_childrenしたがって、問題を解決するには、変数がなぜそうであるかを理解する必要がnilあります(children変数はそうではありません)。

これは明らかに_family、親をシャドウする変数を使用しているためです。したがって、この行NSDictionary *_family = (NSDictionary *)_family;は明らかに内部変数を使用しており_family、 を書いた場合とまったく同じ動作をします。NSDictionary *foo = (NSDictionary *)foo;foonilnil

_family内部変数に別の名前を使用して、2 番目_familyの変数が外部_family変数を覆い隠しないようにすると、問題は解決します。idまたは、キャストせずにメソッドを呼び出すことに問題がないため、この行を完全に削除してください(id実際にはそれが目的です)

于 2012-10-25T16:15:31.323 に答える