在嵌套字典和数组中防御nil值的最有效方法是什么?
我有一个包含字典的数组,其中包含包含字典的数组,因此我有一个方法想要返回这样的内容:
return myArray[0][@"Categories"][0][@"Content"];
目前我有以下内容,但我想知道是否有更干净或更优雅的方法来保护这些嵌套结构:
if (myArray) {
// Add a check for myArray.count > 0
if ([myArray[0] objectForKey:@"Categories"]) {
// Add a check for myArray[@"Categories"].count > 0
if ([myArray[0][@"Categories"][0] objectForKey:@"Content"]) {
return myArray[0][@"Categories"][0][@"Content"];
}
}
}
有更好的方法吗?我有没有漏洞吗?提前谢谢!
编辑:我添加了添加检查数组计数的注释。有了这些添加内容,我可以访问myArray [0] [@“Categories”] [0] [@“Content”]中的对象安全吗?
答案 0 :(得分:4)
数组或词典都不能包含nil
。因此,您正在尝试防止所请求的密钥/索引不存在。
如果字典键不存在,那么你只需返回nil
,任何发送给nil
的邮件都会被“忽略”。
当你有一个数组并且你试图要求一个超出范围的索引时,就会出现问题。所以你应该做的是检查数组的count
,而不是检查密钥是否存在。
答案 1 :(得分:2)
正如其他人所说,NSDictionary和NSArray都不能包含nil
值。但是,它们可以包含(单例)值NSNull
,如果找不到密钥,NSDictionary将从nil
返回objectForKey
。
就“防御性”引用而言,尤其是解码后的JSON,您可能需要进行多次检查。
首先,请确保您确实拥有预期类型的对象(某些JSON提供程序可能会使用isKindOfClass
在一次访问时返回外部数组,而在下一次访问时返回外部字典:if ([iThinkItsADict isKindOfClass:(NSDictionary class)]) { ...
接下来,如果访问数组,请确保您要使用的索引在数组的范围内(请记住,数组可能没有条目):if (arrayIndex < someArray.count) { ...
如果从字典返回值,则可能是nil
,如果找不到该键。因此,您可以使用if (returnedValue != nil) {...
进行测试(尽管请注意,如果使用nil值来“调度”方法调用,则调用将“成功”而不会出现错误但返回零/零。因此,并不总是需要检查为零。)
字典或数组可以返回NSNull,您可以使用if (returnedValue != [NSNull null]) {...
进行检查(有些人会反对您不应该使用==
或!=
来比较对象的相等性,但是应用程序中只有一个NSNull
对象。)
答案 2 :(得分:1)
NSArray
和NSDictionary
都不能有nil
个值。如果您尝试使用一个或插入一个,则会抛出异常。
答案 3 :(得分:0)
我认为使用内省就足够了,检查你得到的对象是数组还是字典,记住nil目标操作被忽略,它们返回零:
if([myArray isKindOfClass: [NSArray class]] && myArray.count)
{ // If myArray is nil isKindOfClass: and count will return 0
if([myArray[0] isKindOfClass: [NSDictionary class]])
{
if([myArray[0][@"Categories"] isKindOfClass: [NSArray class]] &&
[myArray[0][@"Categories"].count)
{ // If it doesn't contain this key, objectForKey: returns nil
// and nil targeted actions are ignored.
if([myArray[0][@"Categories"][0] isKindOfClass: [NSDictionary class]])
{
return myArray[0][@"Categories"][0][@"Content"];
// If the key is not contained in the dictionary it will return nil
}
}
}
}
答案 4 :(得分:0)
这些是我用来抵御JSON解析意外的类别方法,在这些方法中你没有得到规范会引导你期望的数组/字典。
@implementation NSObject (TWXNSObject)
- (BOOL)exists
{
if ([[NSNull null] isEqual:self])
return NO;
return YES;
}
// an empty JSON dictionary comes through as an array
- (BOOL)isDictionary
{
if (!self.exists)
return NO;
if (![self isKindOfClass:[NSDictionary class]])
return NO;
return YES;
}
@end
验证数组访问权限的方法如下所示:
- (BOOL)isIndexValid:(NSUInteger)idx
{
if (!self.exists)
return NO;
if (![self isKindOfClass:[NSArray class]])
return NO;
if (idx >= self.count)
return NO;
return YES;
}
理论上,您可以调出-objectForKeyedSubscript和-objectAtIndexedSubscript的实现,将这些检查透明地添加到下标语法中。实际上,由于NSDictionary和NSArray是类集群,可以预见它迟早会以令人惊讶的方式失败。所以你最好定义像
这样的东西@implementation NSDictionary (TWXNSDictionary)
- (id)safeObjectForKey:(id)key
{
if (self.isDictionary)
return [self objectForKey:key];
return nil;
}
@end
@implementation NSArray (TWXNSArray)
- (id)safeObjectAtIndex:(NSUInteger)idx
{
if ([self isIndexValid:idx])
return [self objectAtIndex:idx];
return nil;
}
@end
并在您访问来自JSON解析的数据结构或可能与您的期望不匹配的其他来源时使用它们。
答案 5 :(得分:0)
我认为好的方法是调动objectAtIndexedSubscript
喜欢这个Objective-C Literals: Negative Array Subscripts
method_exchangeImplementations(
class_getInstanceMethod(self, @selector(objectAtIndexedSubscript:)),
class_getInstanceMethod(self, @selector(BLC_objectAtIndexedSubscript:))
);
- (id)BLC_objectAtIndexedSubscript:(NSInteger)idx {
if (idx < 0) {
idx = [self count] + idx;
}
return [self BLC_objectAtIndexedSubscript:idx];
}