Разница в поведении между UIView.subviews и [NSView subviews]

У меня есть часть кода в приложении iPhone, которая удаляет все подзадачи из подкласса UIView. Это выглядит так:

NSArray* subViews = self.subviews;
for( UIView *aView in subViews ) {
    [aView removeFromSuperview];
}

Это прекрасно работает. На самом деле, я никогда не думал об этом, пока не попробовал почти то же самое в приложении Mac OS X (из подкласса NSView):

NSArray* subViews = [self subviews];
for( NSView *aView in subViews ) {
    [aView removeFromSuperview];
}

Это полностью не работает. В частности, во время выполнения я получаю следующее:

*** Collection <NSCFArray: 0x1005208a0> was mutated while being enumerated.

В итоге я сделал это так:

NSArray* subViews = [[self subviews] copy];
for( NSView *aView in subViews ) {
    [aView removeFromSuperview];
}
[subViews release];

Это хорошо. Меня беспокоит, почему это работает на iPhone?

subviews - свойство копирования:

@property(nonatomic,readonly,copy) NSArray *subviews;

Моя первая мысль была, возможно, @synthesized getters возвращают копию, когда указан атрибут копирования. Документ ясно из семантики копий для сеттеров, но, похоже, не говорит о том, как использовать геттеры (или, по крайней мере, это не очевидно для меня). И на самом деле, делая несколько моих тестов, это явно не так. Что хорошо, я думаю, что возвращение копии было бы проблематичным по нескольким причинам.

Итак, вопрос: как этот код работает на iPhone? NSView явно возвращает указатель на фактический массив subviews, и, возможно, UIView isnt. Возможно, это просто деталь реализации UIView, и я не должен работать над этим.

Может ли кто-нибудь предложить прозрение?

Ответ 1

Это ошибка в -[NSView subviews]. Я мог бы поклясться, что это исправлено для Snow Leopard, но не может найти никаких доказательств.

К счастью, есть более простой способ удалить все подзаголовки:

[self setSubviews:[NSArray array]];

Ответ 2

Лучше использовать

while([self.subviews count] > 0){
    [[self.subviews objectAtindex:0] removeFromSuperview];
}

Надеюсь на эту помощь.

Ответ 3

Да, именно так Apple реализовала его. На mac subviews возвращает фактический (изменяемый) массив, используемый представлением, поэтому вы не можете использовать быстрое перечисление при добавлении или удалении просмотров. Если вы хотите использовать reverseObjectEnumerator, вы могли бы это сделать. Не используйте обычный перечислитель, потому что он может пропускать subviews, когда они перемещаются вверх, чтобы заменить удаленные. В iOS, однако, Apple, похоже, осознала эту проблему и исправила ее, вернув копию автоматически. В объявлении свойства описывается только то, как он установлен, но Apple также выбрала копию для получателя.