如何检查变量是 NSArray 还是 NSMutableArray?
8 回答
*请注意,自编写此答案以来, NS{,Mutable}Array 的实现已更改。结果,isKindOfClass:
现在有效。在什么平台,什么时候,我不知道。
在记录为安全之前,我强烈建议不要编写试图检测集合是可变还是不可变的代码。即使它是安全的,这种设计模式几乎总是表明存在严重的设计缺陷。
(对于有权访问的人)提交 rdar://10355515 要求澄清。
考虑:
int main (int argc, const char * argv[]) {
NSArray *array = [NSArray arrayWithObjects: [NSObject new], nil];
NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects: [NSObject new], nil];
NSLog(@"array's class: %@", NSStringFromClass([array class]));
NSLog(@"mutableArray's class: %@", NSStringFromClass([mutableArray class]));
NSLog(@"array responds to addObject: %@",
[array respondsToSelector: @selector(addObject:)] ? @"YES" : @"NO");
return 0;
}
(我使用的是非空数组,因为空数组NSArray
很常见,Cocoa 提供了一个共享实例作为优化。)
array's class: NSCFArray
mutableArray's class: NSCFArray
array responds to addObject: YES
即既不-isKindOfClass:
检查也不检查addObject:
将起作用。
简而言之,您无法区分 NSArray 和 NSMutableArray。这是设计使然,也是预期的行为。它也适用于NSString
,NSDictionary
和NSSet
(所有这些都有一个可变的子类)。
这可能会让人感到意外。然而,现实情况是,需要检查可变性的设计模式使用起来很混乱,并且会产生大量开销。
例如,如果 test-for-mutability 是一种通用模式,那么 Cocoa 中所有返回NSArray
实例的方法都必须实际返回实例,并且永远不会返回对可能正在使用NSArray
的内部的引用。NSMutableArray
糟糕但技术上准确的建议......
唯一的方法是在/块[unknownArray addObject:someObject]
内调用并捕获不可变时将抛出的异常(实际异常可能是未实现的方法或类是不可变异常)。@try
@catch
NSInternalInconsistencyException
unknownArray
好建议...
简短的回答是永远不要尝试在不可变对象内部查看它是否在内部是可变的。
阻止查看不可变对象的可变性的原因是为了支持像这样工作的类上的方法:
- (NSArray *)internalObjects
{
return myInternalObjects;
}
该对象myInternalObjects
可能是可变的,但是此类上的此方法是说:不要改变我返回给您的内容。这样做可能会有严重的危险。如果该类允许您更改数组,它将具有不同的访问器或修改器方法。
如果您有一个朋友类需要对 myInternalObjects 变量进行可变访问,则声明一个特殊的适配器类别,只有朋友类使用类似的方法导入该适配器类别
- (NSMutableArray *)mutableInternalObjectsArray;
这将允许朋友(您假设它足够聪明,不会违反特殊规则)拥有所需的访问权限,但不会暴露更广泛意义上的可变性。
我会做以下事情 -
if([unkownArray isKindOfClass:[NSMutableArray class]]){
// This is a nsmutable array
}
else if ([unkownArray isKindOfClass:[NSArray class]]){
// This is a nsarray
}
利用 ...
[数组 isKindOfClass:[NSMutableArray 类]]
[Array isKindOfClass:[NSArray 类]]
这将正常工作。
如果您想要一个可变数组,请自己制作:
NSArray *someArray = /* obtain from somewhere, could be mutable, could be immutable */
NSMutableArray *mutableVersion = [someArray mutableCopy]; // definitely mutable
// later
[mutableVersion release];
检查内部类型是一个好主意的情况很少(其他答案已经涵盖了这些)。如果您想要一个可变数组,请不要检查现有数组是否可变,只需制作自己的数组,以便您知道它是可变的。
参见NSObject 的-class
方法:
NSLog(@"myUnknownArray is of type: %@", [myUnknownArray class]);
也可以更直接的检查+class
方法:
BOOL isMutableArray = [myUnknownArray isKindOfClass:[NSMutableArray class]];
这是我的小测试代码,其结果如下。它演示了验证 Foundation 集合对象的可变性的技术。
请注意,可变版本(例如NSMutableArray
)继承自不可变版本(NSArray
),因此需要注意对类的测试。
此外,对仅可变方法的实现的测试似乎可以找到,但可能比类的测试慢一点。
无论如何 - 避免使用@try
和@catch
尝试改变不可变对象,根据 Apple 文档,这些不应该在部署代码中使用 - 因为异常会导致堆栈展开,这特别昂贵,并且还会阻止和取消许多优化。
id arr1 = [NSArray arrayWithObjects:@"Motti",@"name",@55, @"age", nil];
id arr2 = [NSMutableArray arrayWithObjects:@"Motti",@"name",@55, @"age", nil];
NSLog (@"arr1 isKindOfClass NSArray:%@, NSMutableArray:%@", [arr1 isKindOfClass:[NSArray class]] ? @"YES" : @"NO", [arr1 isKindOfClass:[NSMutableArray class]] ? @"YES" : @"NO");
NSLog (@"arr2 isKindOfClass NSArray:%@, NSMutableArray:%@", [arr2 isKindOfClass:[NSArray class]] ? @"YES" : @"NO", [arr2 isKindOfClass:[NSMutableArray class]] ? @"YES" : @"NO");
NSLog (@"arr1 implements insertObject:atIndex:%@", [arr1 respondsToSelector: @selector(insertObject:atIndex:)] ? @"YES" : @"NO");
NSLog (@"arr2 implements insertObject:atIndex:%@", [arr2 respondsToSelector: @selector(insertObject:atIndex:)] ? @"YES" : @"NO");
id dict1 = [NSDictionary dictionaryWithObjectsAndKeys:@"Motti",@"name",@55, @"age", nil];
id dict2 = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"Motti",@"name",@55, @"age", nil];
NSLog (@"dict1 isKindOfClass NSDictoinary:%@, NSMutableDictionary:%@", [dict1 isKindOfClass:[NSDictionary class]] ? @"YES" : @"NO", [dict1 isKindOfClass:[NSMutableDictionary class]] ? @"YES" : @"NO");
NSLog (@"dict2 isKindOfClass NSDictoinary:%@, NSMutableDictionary:%@", [dict2 isKindOfClass:[NSDictionary class]] ? @"YES" : @"NO", [dict2 isKindOfClass:[NSMutableDictionary class]] ? @"YES" : @"NO");
NSLog (@"dict1 implements setObject:forKey:%@", [dict1 respondsToSelector: @selector(setObject:forKey:)] ? @"YES" : @"NO");
NSLog (@"dict2 implements setObject:forKey:%@", [dict2 respondsToSelector: @selector(setObject:forKey:)] ? @"YES" : @"NO");
和结果打印输出:
14:30:42.2683 Test[381:286] arr1 isKindOfClass NSArray:YES, NSMutableArray:NO
14:30:42.2683 Test[381:286] arr2 isKindOfClass NSArray:YES, NSMutableArray:YES
14:30:42.2684 Test[381:286] arr1 implements insertObject:atIndex:NO
14:30:42.2684 Test[381:286] arr2 implements insertObject:atIndex:YES
14:30:42.2684 Test[381:286] dict1 isKindOfClass NSDictoinary:YES, NSMutableDictionary:NO
14:30:42.2685 Test[381:286] dict2 isKindOfClass NSDictoinary:YES, NSMutableDictionary:YES
14:30:42.2686 Test[381:286] dict1 implements setObject:forKey:NO
14:30:42.2687 Test[381:286] dict2 implements setObject:forKey:YES
你必须使用:
[yourArray isKindOf: [NSArray class]]
因为类的简单比较可能会失败,因为您的数组可能不是 NSArray 而是其他一些低级类型。您还可以检查您的阵列是否响应您需要的方法。
[yourArray respondsToSelector: @selector(addObject:)]