2010-07-18 2 views
6

Мне недавно сказали, что кастинг [NSNull null] до (NSString*) был «ужасным».NSDictionary stringForKey: и литье [NSNull null] в NSString *

Однако метод -[NSDictionary stringForKey:] вернет [NSNull null], если ключ не находится в словаре, и если я не выполняю бросок, компилятор кричит на меня.

Я что-то не хватает?

EDIT:

Моя ошибка ... и я думаю, что я мог бы начинаю видеть проблему ...

Как все уже отмечалось, нет stringForKey: метод на NSDictionary, да Я думал о том, по умолчанию пользователя, когда я задал вопрос ... так что я вернулся и посмотрел на то, что я делаю ... вот это:

NSString * someValue = (NSString*)[myDictionary objectForKey:@"key"]; 
if (someValue == (NSString*)[NSNull null]) ... 

, если я не делаю бросок в в этом случае я получаю следующую ошибку:

warning: comparison of distinct Objective-C types 'struct NSNull *' and 'struct NSString *' lacks a cast

Кастинг значение «решает» проблему, и я предпочел бы не написать 10 строк, в которых один будет делать ...

это плохо? Где я столкнулся с проблемами?

ТАКЖЕ:

Словари приходят из библиотеки JSON ... и Нуль- действительные значения в этом случае, поэтому, возможно, это не то, что NSDictionary возвращает их, если ключ отсутствует, а то, что ключ , на самом деле, там, и значение на самом деле является нулевым.

+0

Я просто проверил документы, и 'NSDictionary' не появляются * имеют * метод 'stringForKey:'. Я что-то упускаю? –

ответ

14

NSDictionary не возвращает экземпляры NSNull, если вы или какой-либо другой источник не поставили их там. Кроме того, NSDictionary не отвечает на -stringForKey:. Возможно, вы думаете о NSUserDefaults?

Если у вас есть экземпляр NSDictionary, и все, что вы хотите сделать, это получить строку из него, вы можете попробовать что-то вроде следующего кода.

NSString *MyGetString(NSDictionary *dict, id key, NSString *fallback) { 
    id result = [dict objectForKey: key]; 

    if (!result) 
     result = fallback; 
    else if (![result isKindOfClass: [NSString class]]) 
     result = [result description]; 

    return result; 
} 

Передаёт строку, как резервный fallback --the функция будет использовать, что если объект не связан с ключом key.

+0

Спасибо! Я отредактировал свой вопрос, чтобы уточнить ... и я думаю, что ваш ответ велик ... но мне все еще интересно, есть ли что-то по своей сути «плохое» в отношении решения, которое у меня уже есть (для моего собственного назидания). – Steve

+3

Если вы имеете в виду бросать 'NSNull' на' NSString', это ужасно, потому что это похоже на вызов собаки рыбой. Да, они оба находятся в одной иерархии (подклассы «NSObject» против видов в * Chordata *), но они очень разные. --- Если вместо этого вы имеете в виду * сравнение * двух объектов разных типов (как в вашем обновленном вопросе), вы можете вместо этого использовать оба параметра 'id', которые оба они (так что это безопасное сравнение). '(id) someValue == (id) [NSNull null]' будет действительным. Лучше всего было бы хранить словарь в 'id' для начала, и только сделать приведение к' NSString' * после * вы проверяете тип. –

+0

Спасибо! Я добавил категорию NSDictionary, которая делает то, что вы предлагаете, и все еще держит все просто для меня. Очень признателен. – Steve

7

Как показывает код Джонатана, лучше использовать метод isKindOfClass:. Он защитит вас, даже если вы измените метод извлечения объекта и т. Д., И это более точная проверка.

Также помните, что [someValue isKindOfClass:[NSString class]] возвращается YES только тогда, когда someValue не nil И это в NSString, что означает, что также достаточно для тестирования nil значений.

+0

спасибо за эту приятную простую, чистую, 1 строку кодового решения – Slee

6

Отливка отличная, потому что NSNullне a NSString. Кастинг не изменяет фактический тип объекта, на который он указывает, он только изменяет отношение компилятора, um, к нему.

В показанном конкретном случае, где только , что вы собираетесь делать с объектом, является прямым испытанием для равенства указателя, ваш подход будет работать, но это источник потенциальных будущих ошибок. Такой хриплый бросок, чтобы отключить предупреждения компилятора, вызывает беспокойство, и его следует избегать, если есть лучший способ.

Реальная проблема, конечно, является предыдущей литой, где NSObject* от objectForKey: отливает прямо на NSString* без вопросов. В настоящее время вы можете уйти от него, потому что считаете, что вам гарантировано получить только строки или NSNull, но что произойдет, если библиотека JSON изменится или просто не сможет выполнить этот контракт? Вы бы действительно лучше делать что-то вроде:

NSObject* someValue = [myDictionary objectForKey:@"key"]; 
NSString* someStr = nil; 
if (someValue == [NSNull null]) 
    ... 
else if ([someValue isKindOfClass:[NSString class]]) 
    someStr = (NSString*) someValue; 
.... 
+0

Спасибо! К сожалению, я не могу дать нескольким людям кредит на ответ, поэтому я должен был отдать его Джонатану за аналогичный (и более ранний) ответ. – Steve

1

Хороший тест для проверки NULL, особенно при тестировании проанализированных результатов от сетевых интерфейсов (JSON особенно) является isEqual: метод. Я проверить как наличие, так и для полезной стоимости с использованием ...

NSString *someValue = [response objectForKey:MyAPIKeySomeValue]; 
if (!!someValue && ![someValue isEqual:[NSNull null]]) { 
    // optionally test for class type 
    // use someValue 
} 
else { 
    // error out for required values; skip for optional values 
} 

На более общем плане, если вы ожидаете JSON в определенном формате, и API не удовлетворяет этому требованию, вы могли бы считают, что кандидат на обертку в @try/@catch. Затем вы можете обрабатывать ненормальный/незаконный формат ответа и устранять жесткие сбои в отношении недопустимых форматов данных.

Если формат не определен, вы можете позволить своим процедурам разбора более грациозно скомпоновать, построив содержательный NSError.

+0

это здорово. Но почему бы не использовать «if (someValue ..)» вместо двойной инверсии на переменную somevalue? –

2

Вы можете использовать расширение этой категории, чтобы обернуть эту логику вверх. это на GitHub

NSDictionary + Verified.h:

// NSDictionary+Verified.h 
// 
// Created by alexruperez on 08/05/13. 
// Copyright (c) 2013 alexruperez. All rights reserved. 
// 

#import <Foundation/Foundation.h> 

@interface NSDictionary (Verified) 

- (id)verifiedObjectForKey:(id)aKey; 

@end 

NSDictionary + Verified.m:

NSDictionary+Verified.m 

#import "NSDictionary+Verified.h" 

@implementation NSDictionary (Verified) 

- (id)verifiedObjectForKey:(id)aKey 
{ 
    if ([self objectForKey:aKey] && ![[self objectForKey:aKey] isKindOfClass:[NSNull class]]) return [self objectForKey:aKey]; 
    return nil; 
} 

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