Как разрешить 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
?)
ОБНОВЛЕНИЕ: это то, что я наконец сделал:
[custData setObject: ([rs stringForColumnIndex:2] != nil? [rs stringForColumnIndex:2]:@"") forKey: @"email"];
Ответы
Ответ 1
Существует объект nil
NSNull
, который построен специально для представления nil
в ситуациях, когда "plain" nil
неприемлемо. Если вы замените nil
на [NSNull null]
объект, NSDictionary
возьмет их. Однако вам нужно будет проверить NSNull
на выходе.
Обратите внимание, что это важно только тогда, когда вы должны различать значение, которое не задано, и значение, установленное на nil
. Если ваш код таков, что он может интерпретировать недостающее значение как nil
, вам вообще не нужно использовать NSNull
.
Ответ 2
Это невозможно с чистым NSMutableDictionary, и в большинстве случаев вы хотите преобразовать значения nil в [NSNull null]
или просто опустить их из словаря. Иногда (очень редко), однако, удобно указывать значения nil, и в этих случаях вы можете использовать CFMutableDictionary с пользовательскими обратными вызовами.
Если вы пойдете так, я рекомендую вам использовать API CoreFoundation для всех обращений, например. CFDictionarySetValue
и CFDictionaryGetValue
.
Однако, если вы знаете, что вы делаете, вы можете использовать бесплатный мостик и наложить этот CFMutableDictionary на NSMutableDictionary или NSDictionary. Это может быть полезно, если у вас есть куча помощников, которые принимают NSDictionary, и вы хотите использовать их в своем модифицированном словаре с поддержкой nil. (Конечно, убедитесь, что помощники не удивлены значениями nil.)
Если вы используете мост, обратите внимание, что:
1) Набор NSMutableDictionary повышает ошибки на значения nil перед мостом, поэтому вам нужно использовать CFDictionarySetValue для установки значений, которые потенциально равны нулю.
2) технически мы нарушаем контракт NSMutableDictionary здесь, и все может порваться (например, в будущих обновлениях ОС)
3) много кода будет очень удивлен, найдя значения nil в словаре; вы должны передавать только мостовые франкенд-словари в код, который вы контролируете
См. сообщение о смешной фишке на бесплатном мостике для объяснения того, почему мостовой 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
Ответ 3
Мне нужно было установить значение 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;
}