如何允许NSMutableDictionary接受'nil'值?

时间:2012-06-01 20:12:57

标签: objective-c

我有这样的声明:

 [custData setObject: [rs stringForColumnIndex:2]  forKey: @"email"];

从SQLite3 d / b获得的[rs stringForColumnIndex:2]的值为nil。该应用程序崩溃给我错误:

NSCFDictionary setObject:forKey:]: attempt to insert nil value (key: email)'

有没有办法防止这种情况发生? (比如NSMutableDictionary的设置?)

更新:这是我最终做的:

[custData setObject: ([rs stringForColumnIndex:2] != nil? [rs stringForColumnIndex:2]:@"") forKey: @"email"];

3 个答案:

答案 0 :(得分:36)

有一个名为NSNull的非nil对象,专门用于在“普通”nil不可接受的情况下代表nil。如果您将nil替换为[NSNull null]个对象,NSDictionary将会接受它们。不过,您需要在出路时检查NSNull

请注意,只有在必须区分未设置的值和设置为nil的值时,这才重要。如果您的代码能够解释缺失值为nil,则根本不需要使用NSNull

答案 1 :(得分:5)

使用纯NSMutableDictionary是不可能的,并且在大多数情况下,您希望将nil值转换为[NSNull null]或者只是从字典中省略它们。但是,有时(很少),允许nil值很方便,在这种情况下,您可以将CFMutableDictionary与自定义回调一起使用。

如果你这样做,我建议你使用CoreFoundation API进行所有访问,例如CFDictionarySetValueCFDictionaryGetValue

但是,如果您知道自己在做什么,可以使用免费桥接并将CFMutableDictionary强制转换为NSMutableDictionary或NSDictionary。如果您有一堆接受NSDictionary的帮助程序,并且您希望在修改后的nil字典上使用它们,这可能很有用。 (当然,确保帮助者不会对零值感到惊讶。)

如果您进行过桥接,请注意:

1)NSMutableDictionary setter在桥接之前引发nil值的错误,因此您需要使用CFDictionarySetValue来设置可能为nil的值。

2)从技术上讲,我们在这里违反了NSMutableDictionary的合同,事情可能会中断(例如在未来的操作系统更新中)

3)在字典中找到nil值会让很多代码感到非常惊讶;你应该只将桥接的法定代码传递给你控制的代码

有关桥接CFDictionary与NSDictionary行为不同的原因的解释,请参阅ridiculousfish's post on toll-free bridging

示例:

#import <Foundation/Foundation.h>

const void *NullSafeRetain(CFAllocatorRef allocator, const void *value) {
    return value ? CFRetain(value) : NULL;
}
void NullSafeRelease(CFAllocatorRef allocator, const void *value) {
    if (value)
        CFRelease(value);
}
const CFDictionaryValueCallBacks kDictionaryValueCallBacksAllowingNULL = {
    .version = 0,
    .retain = NullSafeRetain,
    .release = NullSafeRelease,
    .copyDescription = CFCopyDescription,
    .equal = CFEqual,
};

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        CFMutableDictionaryRef cfdictionary = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kDictionaryValueCallBacksAllowingNULL);
        CFDictionarySetValue(cfdictionary, @"foo", @"bar");
        CFDictionarySetValue(cfdictionary, @"boz", nil);

        NSMutableDictionary *dictionary = CFBridgingRelease(cfdictionary);
        NSLog(@"dictionary[foo] = %@", dictionary[@"foo"]);
        NSLog(@"dictionary[foo] = %@", dictionary[[@"fo" stringByAppendingString:@"o"]]);
        NSLog(@"dictionary[boz] = %@", dictionary[@"boz"]);
        NSLog(@"dictionary = %@", dictionary);
        NSLog(@"(dictionary isEqualTo: dictionary) = %d", [dictionary isEqualToDictionary:dictionary]);
    }
    return 0;
}

输出:

dictionary[foo] = bar
dictionary[foo] = bar
dictionary[boz] = (null)
dictionary = {
    boz = (null);
    foo = bar;
}
(dictionary isEqualTo: dictionary) = 1

答案 2 :(得分:0)

我需要将NSDictionary值设置为NSUserDefaults可能已设置或未设置的值。

我所做的是将值包装在stringwithFormat调用中。这两个值尚未设置,因此从null开始。当我在没有stringwithFormat调用的情况下运行时,应用程序崩溃了。所以我这样做了,在我的情况下工作了。

-(NSDictionary*)userDetailsDict{
NSDictionary* userDetails = @{
                              @"userLine":[NSString stringWithFormat:@"%@",[[NSUserDefaults standardUserDefaults]stringForKey:kSelectedLine] ],
                              @"userDepot":[NSString stringWithFormat:@"%@",[[NSUserDefaults standardUserDefaults]stringForKey:@"kSelected Duty Book"]]
                              };

return userDetails;
}