Быстрое перечисление на NSArray разных типов - PullRequest
5 голосов
/ 10 декабря 2011

У меня есть этот вопрос здесь (а также другие вопросы по SO) и документы Apple о коллекциях Objective-C и быстром перечислении. Что не ясно, так это если NSArray заполнен разными типами, и цикл создается как:

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

Что именно здесь происходит? Будет ли цикл пропустить что-либо, что не является NSString? Например, если (ради аргумента) в массиве находится UIView, что произойдет, когда цикл встретит этот элемент?

Ответы [ 5 ]

9 голосов
/ 10 декабря 2011

Почему вы хотите это сделать?Я думаю, что это может привести к ошибкам и непреднамеренному поведению.Если ваш массив заполнен различными элементами, используйте это вместо:

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() таким способом вызовет метод - (NSString *)description в соответствии с протоколом NSObject , которому может соответствовать или не соответствовать класс, на который ссылается внутри массива.Если это соответствует, это напечатает это.В противном случае произойдет сбой.

3 голосов
/ 10 декабря 2011

Вы должны понимать, что указатель в obj-c не имеет информации о типе.Даже если вы напишите NSString*, это всего лишь проверка компиляции.Во время выполнения все является просто id.

Среда выполнения Obj-c никогда не проверяет, принадлежат ли объекты данному классу.Вы можете поместить NSNumbers в указатели NSString без проблем.Ошибка появляется только при попытке вызвать метод (отправить сообщение), который не определен для объекта.

Как работает быстрое перечисление?Это точно так же, как:


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

    [...]
}

Это просто быстрее, потому что работает на более низком уровне.

2 голосов
/ 10 декабря 2011

Я только что попробовал быстрый пример ... Вот мой код.

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

Однако, если я попытаюсь войти -length в NSString ...

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

... он выдает NSInvalidArgumentException, потому что NSNumber не отвечает на селектор -length.Короче говоря, Objective-C дает вам много веревки.Не вешайся на это.

2 голосов
/ 10 декабря 2011

Интересный вопрос. Наиболее общий синтаксис для быстрого перечисления:

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 101 * для быстрого перечисления , но вы можете проверить это на примере и посмотреть, что произойдет.

1 голос
/ 31 января 2013

Поскольку все NSObject отвечают на isKindOfClass, вы все равно можете поддерживать приведение к минимуму:

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