Objective c 不同类型NSArray的快速枚举

Objective c 不同类型NSArray的快速枚举,objective-c,fast-enumeration,Objective C,Fast Enumeration,我有这个问题(以及其他问题),还有关于Objective-C集合和快速枚举的Apple文档。不清楚的是,如果一个NSArray填充了不同的类型,并且创建了如下循环: for ( NSString *string in myArray ) NSLog( @"%@\n", string ); 这里到底发生了什么?循环是否会跳过任何不是NSString的内容?例如,如果(为了论证)数组中有一个UIView,当循环遇到该项时会发生什么情况?有趣的问题。快速枚举最通用的语法是 for ( NSO

我有这个问题(以及其他问题),还有关于Objective-C集合和快速枚举的Apple文档。不清楚的是,如果一个
NSArray
填充了不同的类型,并且创建了如下循环:

for ( NSString *string in myArray )
    NSLog( @"%@\n", string );

这里到底发生了什么?循环是否会跳过任何不是
NSString
的内容?例如,如果(为了论证)数组中有一个
UIView
,当循环遇到该项时会发生什么情况?

有趣的问题。快速枚举最通用的语法是

for ( NSObject *obj in myArray )
    NSLog( @"%@\n", obj );
我相信通过这样做

for ( NSString *string in myArray )
    NSLog( @"%@\n", string );
相反,您只需将每个对象强制转换为
NSString
。也就是说,我相信以上是等同于

for ( NSObject *obj in myArray ) {
    NSString *string = obj;
    NSLog( @"%@\n", string );
}

我在苹果的文章中找不到对这一点的确切提及,但你可以在一个例子中查看一下,看看会发生什么。

你为什么要这样做?我认为这会导致错误和意外行为。如果阵列中填充了不同的元素,请改用以下方法:

for (id object in myArray) {
    // Check what kind of class it is
    if ([object isKindOfClass:[UIView class]]) {
       // Do something
    }
    else {
       // Handle accordingly
    }
}
您在示例中所做的实际上与

for (id object in myArray) {
    NSString *string = (NSString *)object;
    NSLog(@"%@\n", string);
}

仅仅因为您将
对象
强制转换为
(NSString*)
,并不意味着
字符串
实际上将指向一个
NSString
对象。以这种方式调用
NSLog()
将根据调用
-(NSString*)description
方法,数组中引用的类可能符合,也可能不符合。如果符合,它会打印出来。否则,它将崩溃。

我刚刚尝试了一个快速示例。。。这是我的密码

NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:1];
NSNumber *number = [NSNumber numberWithInteger:6];
[array addObject:number];
[array addObject:@"Second"];
现在,如果我只是记录对象,没有问题。
NSNumber
实例被强制转换为
NSString
,但这两种方法都响应
-description
,因此这不是问题

for (NSString *string in array)
{
    NSLog(@"%@", string);
}
但是,如果我尝试在
NSString
上记录
-length

for (NSString *string in array)
{
    NSLog(@"%i", string.length);
}

。。。它抛出一个
NSInvalidArgumentException
,因为
NSNumber
不响应
-length
选择器。长话短说,Objective-C给了你很大的束缚。不要自责。

您必须了解obj-c中的指针没有类型信息。即使您编写
NSString*
,这也只是一个编译检查。在运行时,一切都只是一个
id

Obj-c运行时从不检查对象是否属于给定类。您可以毫无问题地将NSNumbers放入NSString指针。只有在尝试调用对象上未定义的方法(发送消息)时,才会出现错误

快速枚举是如何工作的?这与:


for (NSUInteger i = 0; i < myArray.count; i++) {
    NSString* string = [myArray objectAtIndex:i];

    [...]
}

对于(整数i=0;i

因为它在较低的级别上运行,所以速度更快。

因为所有NSObject对类的响应都是类的,所以您仍然可以将强制转换保持在最低限度:

for(NSString *string in myArray) {
    if (![string isKindOfClass:[NSString class]])
        continue;
    // proceed, knowing you have a valid NSString *
    // ...
}

问题更多的是对Objective-C的深入理解,而不是实际做这样的事情。另外,我最近一直在使用C#,在这里可以指定进入集合的类型。True。我会更新我的答案,但基本上,你比我快。对你的帖子有一点澄清:
NSObject
本身符合
NSObject
协议,因此任何继承自
NSObject
类的对象(即,除了消失的少数之外)也符合协议。@JoshCaswell很好看。我只是想更深入地解释引擎盖下面发生了什么,并举一个可能是有车的、意外行为的例子。也许这个特定实例不是最常见的问题,但最好记住实际调用的是什么,以避免潜在的崩溃,因为即使是像
NSLog()
这样看似无辜的调用也不会总是适用于非类型化或错误类型的对象。快速枚举对类“言而有信”@John提供了处理具有未知类成员的数组的最佳解决方案。没有发生隐式筛选,可能导致调用接收方无法处理的方法。。。Crashnsumber不是作为NSString铸造的。NSNumber和NSString都是NSObject的后代,正如您所说,NSLog调用了其中的
description
。但这不涉及演员阵容。哦,我明白了。我想我只是把强制转换和代码完成特性混淆了。在编写示例时,我没有收到关于
NSNumber
实例上的消息
-length
的警告。啊!好的…我现在也明白你的意思了。正在将NSNumber作为NSString转换为
字符串
。我还以为你是说NSLog在做演员(从提到
description
开始)。我的错!对于(arr中的id obj)
(因为它涵盖了(极少数)不从
NSObject
下降的对象)来说,“最通用”的版本不是
吗?