Подтвердить что ты не робот

Как разрешить 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"];
4b9b3361

Ответ 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;
}