2012-06-01 2 views
18

У меня это утверждение:Как разрешить NSMutableDictionary принимать значения «nil»?

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

, где [rs stringForColumnIndex:2] получают из SQLite3 D/B имеет значение nil. Авария приложения вызывает ошибку:

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

Есть ли способ предотвратить это? (Например, настройки для NSMutableDictionary?)

UPDATE: это то, что я наконец-то:

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

Это выглядит как хорошее решение для меня. @ "- это хороший способ сохранить заполнители для ключей, когда нет допустимого значения, иначе вы можете иметь такую ​​же проблему, когда позже будете получать значения для ключей. – Miek

+0

В качестве ярлыка: custData [@ "email"] = [rs stringForColumnIndex: 2]?: @ ""; – gnasher729

ответ

35

Существует не- nil объект под названием NSNull, который построен специально для представления nil с в тех ситуациях, когда «обычный «nil неприемлемо. Если вы замените nil с объектом [NSNull null], то их возьмут NSDictionary. Тем не менее, вам нужно будет проверить на NSNull.

Обратите внимание, что это важно только тогда, когда вы должны различать значение, которое не установлено, и значение, установленное на nil. Если ваш код таков, что он может интерпретировать отсутствующее значение как nil, вам не нужно использовать NSNull.

+0

Я устанавливаю значения в NSMutableDictionary, поэтому я могу передать массив данных методу (Objective-c). Таким образом, метод может обрабатывать нуль, но, по-видимому, словарь не может. Поэтому нет настроек, которые я могу установить, чтобы позволить «ноль» ключи, да? Возможно, придется использовать условный оператор? – SpokaneDude

+0

@ spokane-dude Нет, к сожалению, нет настроек, которые позволят вам хранить 'nil' в коллекциях какао. Если вы используете условный оператор, есть расширение, которое позволяет немного сократить код (см. [Эта ссылка] (http://stackoverflow.com/a/5471573/335858)). – dasblinkenlight

+0

Это то, что я, наконец, сделал (см. Обновленный вопрос выше), который работает (по крайней мере, предотвращает сбой приложения), я проверю сторону метода на пустой ключ. Спасибо за ваше время. – SpokaneDude

5

Это невозможно с чистым NSMutableDictionary, и в большинстве случаев вы хотите преобразовать значения nil в [NSNull null] или просто опустить их из словаря. Иногда (очень редко), однако, удобно указывать значения nil, и в этих случаях вы можете использовать CFMutableDictionary с пользовательскими обратными вызовами.

Если вы идете этим путем, я рекомендую вам использовать API CoreFoundation для всех видов доступа, например. CFDictionarySetValue и CFDictionaryGetValue.

Однако, если вы знаете, что делаете, вы можете использовать бесплатный мостик и отбросить этот CFMutableDictionary в NSMutableDictionary или NSDictionary. Это может быть полезно, если у вас есть куча помощников, которые принимают NSDictionary, и вы хотите использовать их в своем модифицированном словаре с поддержкой nil. (Конечно, убедитесь, что помощники не удивлены нулевыми значениями.)

Если вы делаете мостики, обратите внимание, что:

1) NSMutableDictionary сеттер вызывает ошибки на нулевых значениях перед тем преодоление, так что вам нужно использовать CFDictionarySetValue для установки значений, которые потенциально равны нулю.

2) технически, мы нарушив договор о NSMutableDictionary здесь, и вещи могут нарушить (например, в будущих обновлениях ОС)

3) много кода будет очень удивлен, чтобы найти нулевые значения в словаре ; вы должны передавать только мостовые франкенд-словари в код, который вы контролируете

См. ridiculousfish's post on toll-free bridging для объяснения причин, по которым мостовой CFDictionary ведет себя иначе, чем NSDictionary.

Пример:

#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 
+0

CFEqual сбой с параметрами NULL, ваш пример может работать, если внешний код проверяет равенство указателя перед вызовом этой функции, но если вы должны были создать копию словарь, но установите ключ boz на допустимое значение, а затем сравните два словаря, я думаю, что он сработает, вместо того, чтобы возвращать false. Для CFEqual вам понадобится нуль-безопасная оболочка для присвоения свойства ValueCallBacks. –

0

мне нужно, чтобы установить значение NSDictionary на тот, который может или не может быть установлен еще от NSUserDefaults.

Что я сделал, это обернуть значения в вызове stringwithFormat. Оба значения еще не установлены, поэтому начинаются с нуля. Когда я запускаю без вызова stringwithFormat, приложение отключается. Поэтому я сделал это и в своей ситуации работал.

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

return userDetails; 
} 
+0

Я верю это просто переносит значения 'nil' в пустую строку. Вы также можете сделать' value?: @ "" '(что кратковременно для' value? value: @ "" '. –

Смежные вопросы