2012-03-28 2 views
14

여기 코드 것 생성하지 : 당신이 볼 수 있듯이, 나는 그 때 정확히 동일한 JSON에서 빈 목록으로 JSON을 두 번 하나를 구문 분석 방법 등을 전화 드렸습니다NSJSONSerialization는 변경 가능한 용기를

NSError *parseError; 
NSMutableArray *listOfObjects = [NSJSONSerialization JSONObjectWithData:[@"[]" dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&parseError]; 
NSLog(@"Is mutable? %li", [listOfObjects isKindOfClass:[NSMutableArray class]]); 

listOfObjects = [NSJSONSerialization JSONObjectWithData:[@"[[],{}]" dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&parseError]; 
NSLog(@"Is mutable? %li", [listOfObjects isKindOfClass:[NSMutableArray class]]); 

안에있는 객체가있는 목록. 여기서 결과는 다음과 같습니다

Is mutable? 0 
Is mutable? 1 

문제는 NSJSONSerialization가 비어 목록에 대한 변경 가능한 컨테이너를 생성 할 수있는 옵션을 수행하지 않는 것이다. 나에게 버그처럼 보일지도 모르지만, 아마도 나는 오해하고있다.

아이디어가 있으십니까?

+0

NSMutableDictionary로 나오지 않는다고 확신합니까? – Zalykr

+0

디버그 콘솔에서'po [[listOfObjects class] superclass]'를 실행할 때의 결과는 무엇입니까? – warrenm

+1

Mac OS X 10.7에서이 문제를 확인할 수 있습니다 - 빈 배열 만 영향을받는 것 같습니다. 10.8에서 수정 된 것 같습니다. – blutfink

답변

13

이 예상대로 바로 작동합니다

NSString *s = @"{ \"objs\": [ \"a\", \"b\" ] }";  
NSData *d = [NSData dataWithBytes:[s UTF8String] length:[s length]]; 
id dict = [NSJSONSerialization JSONObjectWithData:d options:NSJSONReadingMutableContainers error:NULL]; 

NSLog(@"%@", dict); 

[[dict objectForKey:@"objs"] addObject:@"c"]; 

NSLog(@"%@", dict); 
NSLog(@"%@", [[dict objectForKey:@"objs"] class]); 

다음은 콘솔 출력입니다 :

2012-03-28 13:49:46.224 ExampleRunner[42526:707] { 
    objs =  (
     a, 
     b 
    ); 
} 
2012-03-28 13:49:46.225 ExampleRunner[42526:707] { 
    objs =  (
     a, 
     b, 
     c 
    ); 
} 
2012-03-28 13:49:46.225 ExampleRunner[42526:707] __NSArrayM 

편집

주 우리는 위의 코드에 다음 줄을 추가하면 그 .. .

NSLog(@"%@", [[dict objectForKey:@"objs"] superclass]); 
따라서 영업 이익의 코드가 실제로했던 것을 증명, __NSArrayMNSMutableArray의 개인 서브 클래스임을 밝혀지지 않았다 단지의 경우 ...

2012-03-28 18:09:53.770 ExampleRunner[42830:707] NSMutableArray 

: 363,210

... 우리는 콘솔에 다음과 같은 출력을 얻을 예상대로 작동합니다 (그의 NSLog 성명을 제외하고).

편집

아, 그리고 그 길을, 다음 코드 줄에 의해 ...

NSLog(@"%d", [[dict objectForKey:@"objs"] isKindOfClass:[NSMutableArray class]]); 

... 다음 콘솔 출력 결과 :

2012-03-28 18:19:19.721 ExampleRunner[42886:707] 1 

EDIT (변경된 질문에 응답)

흥미 롭습니다 ... 버그처럼 보입니다. 다음 코드를 감안할 때 :

NSData *dictData2 = [@"{ \"foo\": \"bar\" }" dataUsingEncoding:NSUTF8StringEncoding]; 
id dict2 = [NSJSONSerialization JSONObjectWithData:dictData2 options:NSJSONReadingMutableContainers error:NULL]; 
NSLog(@"%@", [dict2 class]); 
NSLog(@"%@", [dict2 superclass]); 
NSLog(@"%d", [dict2 isKindOfClass:[NSMutableDictionary class]]); 

// This works... 
[dict2 setObject:@"quux" forKey:@"baz"]; 
NSLog(@"%@", dict2); 

NSData *dictData = [@"{}" dataUsingEncoding:NSUTF8StringEncoding]; 
id emptyDict = [NSJSONSerialization JSONObjectWithData:dictData options:NSJSONReadingMutableContainers error:NULL]; 
NSLog(@"%@", [emptyDict class]); 
NSLog(@"%@", [emptyDict superclass]); 
NSLog(@"%d", [emptyDict isKindOfClass:[NSMutableDictionary class]]); 

//...but this fails: 
[emptyDict setObject:@"quux" forKey:@"baz"]; 
NSLog(@"%@", emptyDict); 

다음은 콘솔 출력입니다 :

2012-03-29 09:40:52.781 ExampleRunner[43816:707] NSMutableDictionary 
2012-03-29 09:40:52.782 ExampleRunner[43816:707] 1 
2012-03-29 09:40:52.782 ExampleRunner[43816:707] __NSCFDictionary 
2012-03-29 09:40:52.782 ExampleRunner[43816:707] NSMutableDictionary 
2012-03-29 09:40:52.783 ExampleRunner[43816:707] 1 
2012-03-29 09:40:52.783 ExampleRunner[43816:707] { 
    baz = quux; 
    foo = bar; 
} 
2012-03-29 09:40:52.784 ExampleRunner[43816:707] __NSCFDictionary 
2012-03-29 09:40:52.784 ExampleRunner[43816:707] NSMutableDictionary 
2012-03-29 09:40:52.784 ExampleRunner[43816:707] 1 
2012-03-29 09:40:52.785 ExampleRunner[43816:707] NSException: -[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object 

그래서 빈 배열과이 방법으로 만든 사전이 예상대로 작동하지 않는 것.

+0

[비용 클래스] 여전히 NSArray, 대신 가변 배열, 아니? –

+0

Nope. 첫째, 'NSArray' 인스턴스에 객체를 추가 할 수 없습니다. 둘째, 콘솔 출력을 살펴보면 배열은 실제로'NSMutableArray'의 private 하위 클래스 인'__NSArrayM'의 인스턴스입니다. – jlehr

+0

안녕하세요 회신 주셔서 감사합니다. 지금까지 질문을 업데이트했지만 문제를 올바르게 설명하지 못했다는 것을 알았습니다. 다시 확인할 수 있습니까? –

1

다른 사람은 또한, 버그이을 참조 예를 들어

  1. https://github.com/couchbaselabs/TouchDB-iOS/issues/44 또는
  2. https://github.com/johnlabarge/jlbiosutils/blob/master/jlbiosutils/DynamicProperties.m
  3. .

후자의 경우 공백 사전에 대한 전체 해결 방법을 확인할 수 있습니다 (DynamicGetter(...) 참조). 대신 평소

NSDictionary *object = [NSJSONSerialization JSONObjectWithDataFixed:jsonData options:NSJSONReadingMutableContainers error:&err]; 

가 :

여기
NSDictionary *object = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err]; 
+0

Jens Alfke가 버그라고 생각하는 모든 것은 버그입니다. 링크를 가져 주셔서 감사 드리며, 거의 같은 방식으로 수정했습니다. –

7

다음은이 문제에 대한 내 해결의

BOOL needsWorkaround = YES; 
if (needsWorkaround) 
{ 
    NSMutableDictionary* appState2 = 
     (__bridge_transfer NSMutableDictionary*) 
     CFPropertyListCreateDeepCopy (
      kCFAllocatorDefault, (__bridge void*)appState, 
      kCFPropertyListMutableContainersAndLeaves 
     ); 
    appState = appState2; 
} 
+0

위대한! 받아 들인 대답이어야한다. –

1

것은 내가 할 것입니다 :

#import "NSJSONSerialization+MutableBugFix.h" 

@implementation NSJSONSerialization (NSJSONSerialization_MutableBugFix) 

+ (id)JSONObjectWithDataFixed:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error { 
    id object = [NSJSONSerialization JSONObjectWithData:data options:opt error:error]; 

    if (opt & NSJSONReadingMutableContainers) { 
     return [self JSONMutableFixObject:object]; 
    } 

    return object; 
} 

+ (id)JSONMutableFixObject:(id)object { 
    if ([object isKindOfClass:[NSDictionary class]]) { 
     // NSJSONSerialization creates an immutable container if it's empty (boo!!) 
     if ([object count] == 0) { 
      object = [object mutableCopy]; 
     } 

     for (NSString *key in [object allKeys]) { 
      [object setObject:[self JSONMutableFixObject:[object objectForKey:key]] forKey:key]; 
     } 
    } else if ([object isKindOfClass:[NSArray class]]) { 
     // NSJSONSerialization creates an immutable container if it's empty (boo!!) 
     if (![object count] == 0) { 
      object = [object mutableCopy]; 
     } 

     for (NSUInteger i = 0; i < [object count]; ++i) { 
      [object replaceObjectAtIndex:i withObject:[self JSONMutableFixObject:[object objectAtIndex:i]]]; 
     } 
    } 

    return object; 
} 

@end 

그래서 내가 전화 :

관련 문제