Разница в поведении между UIView.subviews и [NSView subviews] - PullRequest
6 голосов
/ 12 января 2011

У меня есть фрагмент кода в приложении для 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;

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

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

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

Ответы [ 3 ]

13 голосов
/ 12 января 2011

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

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

[self setSubviews:[NSArray array]];
3 голосов
/ 12 января 2011

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

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

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

2 голосов
/ 12 января 2011

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

...