【问题标题】:Fast Enumeration on NSArray of Different Types快速枚举不同类型的 NSArray
【发布时间】:2011-12-09 22:28:19
【问题描述】:

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

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

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

【问题讨论】:

  • 在课堂上快速枚举“言出必行”。 @awfullyjohn 提供了处理包含未知类成员的数组的最佳解决方案。没有发生隐式过滤,可能导致调用接收方无法处理的方法...崩溃

标签: objective-c fast-enumeration


【解决方案1】:

您为什么要这样做?我认为这会导致错误和意外行为。如果您的数组填充了不同的元素,请改用它:

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);
}

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

【讨论】:

  • 问题是对Objective-C的理解比实际做这样的事情更深入。另外,我最近一直在使用 C#,可以指定进入集合的类型。
  • 是的。我会更新我的答案,但基本上,你打败了我。
  • 对您的帖子的一个小说明:NSObject 本身符合 NSObject 协议,因此任何继承自 NSObject 类的对象(即,除了极少数之外的所有对象)也符合协议。
  • @JoshCaswell 很好看。我只是想更深入地解释引擎盖下发生的事情,并举​​一个可能有错误、意外行为的例子。也许这个特定的实例不是最常见的问题,但最好记住实际调用的内容以避免潜在的崩溃,因为即使像 NSLog() 这样看似无辜的调用也不会总是起作用在未键入或键入错误的对象上。
【解决方案2】:

你必须明白,obj-c 中的指针没有类型信息。就算写NSString*,也只是编译检查。在运行时,一切都只是一个id

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

快速枚举如何工作?完全一样:


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

    [...]
}

它只是更快,因为它在较低级别上运行。

【讨论】:

    【解决方案3】:

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

    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 );
    }
    

    我在 Apple 的 documentation for Fast Enumeration 中找不到确切的提及,但您可以查看一个示例,看看会发生什么。

    【讨论】:

    • “最通用”的版本不是for( id obj in arr)(因为它涵盖了(极少数)不是来自NSObject 的对象)?
    【解决方案4】:

    我只是尝试了一个简单的示例...这是我的代码。

    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 给了你很多东西。不要上吊自己。

    【讨论】:

    • NSNumber 没有被转换为 NSString。 NSNumber 和 NSString 都是 NSObject 的后代,就像你说的,NSLog 调用 description。但是没有涉及到演员。
    • 哦,我明白了。我想我只是将强制转换与代码完成功能混淆了。在编写示例时,我没有收到关于在 NSNumber 实例上发送 -length 的警告。
    • 啊!好的……我现在也明白你的意思了。 NSNumber 被作为 NSString 转换为 string。我以为你是在说 NSLog 正在做演员(来自description 的提及)。我的错!
    【解决方案5】:

    由于所有 NSObject 都响应 isKindOfClass,您仍然可以将转换保持在最低限度:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-15
      • 2015-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-19
      • 2013-04-26
      相关资源
      最近更新 更多