如何检查一个变量是NSArray还是NSMutableArray?
如何检查一个变量是NSArray还是NSMutableArray?
注意,自此回答编写以来,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。
简而言之,您无法区分NSArray和NSMutableArray的区别。这是故意设计的行为。这也适用于NSString、NSDictionary和NSSet(它们都有一个可变的子类)。
这可能会让人感到惊讶。但事实上,需要检查是否可变的设计模式很难使用且具有重大开销。
例如,如果测试可变性是一种常见的模式,那么Cocoa中返回NSArray
实例的所有方法都必须实际返回NSArray
实例,并且永远不会返回对内部NSMutableArray
的引用,因为这可能导致结果不可预测。
-isKindOfClass:
是依赖于实现细节。除非文档更新明确说明这样做是API合同的一部分,否则它是不安全的,任何目前可以工作的代码也不能保证三年后仍能工作。 - bbumif([unkownArray isKindOfClass:[NSMutableArray class]]){
// This is a nsmutable array
}
else if ([unkownArray isKindOfClass:[NSArray class]]){
// This is a nsarray
}
不太好但技术上准确的建议...
唯一的方法是在@try
/@catch
块中调用[unknownArray addObject:someObject]
,并捕获如果unknownArray
是不可变的将抛出的NSInternalInconsistencyException
异常(实际异常可能是方法未实现或类是不可变异常)。
好的建议...
简短的答案是永远不要尝试查看不可变对象是否内部可变。
禁止查看不可变对象的可变性的原因是为了支持像这样工作的类的方法:
- (NSArray *)internalObjects
{
return myInternalObjects;
}
myInternalObjects
可能是可变的,但是该类中的这个方法指出: 不要改变我返回给你的内容。这样做可能会带来严重的危险。如果该类允许您更改数组,则它将具有不同的访问器或修改器方法。- (NSMutableArray *)mutableInternalObjectsArray;
NSArray *someArray = /* obtain from somewhere, could be mutable, could be immutable */
NSMutableArray *mutableVersion = [someArray mutableCopy]; // definitely mutable
// later
[mutableVersion release];
在很少的情况下检查内部类型是一个好主意(其他答案已经涵盖了这些情况)。如果你想要一个可变数组,不要检查现有数组是否可变,只需自己创建一个,以确保它是可变的。
NSJSONSerialization writeJSONObject:toStream:options:error:
时,您必须了解对象的可变性。如果您将一个可变字典/数组传递给它而没有指定 NSJSONReadingMutableContainers
选项,这种不一致可能会导致 *** Collection <__NSDictionaryM: 0xabcdef> was mutated while being enumerated
异常。在我的情况下,我们有一个通用的 writeDictionary:(NSDictionary *)dict asJSONToFile:(NSString *)filePath
实用方法。为了保证一致性,唯一的方法是始终创建一个可变副本,并始终设置 NSJSONReadingMutableContainers
。 - Goffredo使用 ...
[Array isKindOfClass:[NSMutableArray class]]
[Array isKindOfClass:[NSArray class]]
这将正常工作。
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
请参见NSObject的-class
方法:
NSLog(@"myUnknownArray is of type: %@", [myUnknownArray class]);
您还可以使用 +class
方法直接进行更多检查:
BOOL isMutableArray = [myUnknownArray isKindOfClass:[NSMutableArray class]];
NSMutableArray
,则 [object isKindOfClass:[NSArray class]]
也会返回 YES
。 @Alexander @Alex - Burhanuddin Sunelwala你必须使用:
[yourArray isKindOf: [NSArray class]]
由于您的数组可能不是NSArray而是某些其他低级类型,因此对类的简单比较可能会失败。您还可以检查数组是否响应您需要的方法。
[yourArray respondsToSelector: @selector(addObject:)]