Вопрос по objective-c – Как разрешить NSMutableDictionary принимать значения 'nil'?

19

У меня есть это утверждение:

 [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"];
Как ярлык: custData [@ "email"] = [rs stringForColumnIndex: 2]?: @ ""; gnasher729
Это похоже на хорошее решение для меня. @ "" - это хороший способ сохранить заполнители для ключей, когда нет действительного значения, в противном случае у вас может возникнуть та же проблема при доступе к значениям для ключей позже. Miek

Ваш Ответ

3   ответа
36

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

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

Я устанавливаю значения в NSMutableDictionary, чтобы я мог передать массив данных методу (Objective-c). Таким образом, метод может обрабатывать ноль, но, очевидно, словарь не может. Так что я не могу установить параметр, позволяющий использовать «ноль» ключи, а? Вероятно, придется использовать условный оператор? SpokaneDude
@ spokane-dude Нет, к сожалению, нет настроек, которые позволили бы вам хранитьnil в коллекциях какао. Если вы используете условный оператор, есть расширение, которое позволяет несколько сократить код (см.эта ссылк). dasblinkenlight
Это то, что я наконец-то сделал (см. Обновленный вопрос выше), и это работает (по крайней мере, предотвращает сбой приложения; я проверю на стороне метода наличие пустого ключа. Спасибо за ваше время. SpokaneDude
@ spokane-dude Вы также должны взглянуть на другой ответ, этона самом дел отвечает на ваш вопрос. user529758
5

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

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

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

Если вы используете мост, обратите внимание:

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

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

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

Видеть смешной пост о бесплатном мостовом соединении для объяснения того, почему мостовой 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;
}

outputs:

dictionary[foo] = bar
dictionary[foo] = bar
dictionary[boz] = (null)
dictionary = {
    boz = (null);
    foo = bar;
}
(dictionary isEqualTo: dictionary) = 1
CFEqual завершится с параметрами NULL; ваш пример может сработать, если внешний код проверяет равенство указателей перед вызовом этой функции, но если вы создадите копию словаря выше, но установите для ключа boz правильное значение, то сравните два словаря, я полагаю, что это приведет к сбою вместо возвращая ложь. Я думаю, вам нужна нулевая безопасная оболочка для CFEqual, чтобы присвоить свойству ValueCallBacks. Carl Lindberg
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;
}
Я считаю, что это просто транспонированиеnil значения в пустую строку. Вы также можете сделатьvalue ?: @"" (сокращение отvalue ? value : @"". Chris Nolet

Похожие вопросы