开发者

Behavior difference between UIView.subviews and [NSView subviews]

I have a piece of code in an iPhone app, which removes all subviews from a UIView subclass. It looks like this:

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

This works fine. In fact, I never really gave it much thought until I tried nearly the same thing in a Mac OS X app (from an NSView subclass):

NSArray* subViews = [self subviews];
for( NSView *aView in subViews ) {
 开发者_开发知识库   [aView removeFromSuperview];
}

That totally doesn’t work. Specifically, at runtime, I get this:

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

I ended up doing it like so:

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

That's fine. What’s bugging me, though, is why does it work on the iPhone?

subviews is a copy property:

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

My first thought was, maybe @synthesize’d getters return a copy when the copy attribute is specified. The doc is clear on the semantics of copy for setters, but doesn’t appear to say either way for getters (or at least, it’s not apparent to me). And actually, doing a few tests of my own, this clearly does not seem to be the case. Which is good, I think returning a copy would be problematic, for a few reasons.

So the question is: how does the above code work on the iPhone? NSView is clearly returning a pointer to the actual array of subviews, and perhaps UIView isn’t. Perhaps it’s simply an implementation detail of UIView, and I shouldn’t get worked up about it.

Can anyone offer any insight?


This is a bug in -[NSView subviews]. I could have sworn it was fixed for Snow Leopard, but can't find any evidence now.

Fortunately, there's a simpler way to remove all subviews:

[self setSubviews:[NSArray array]];


Better use

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

Hope this help.


Yes, it is just how Apple implemented it. On the mac, subviews returns the actual (mutable) array used by the view, so you cannot use fast enumeration while adding or removing views. If you want to use the reverseObjectEnumerator, you might be able to. Do not use the normal enumerator because it might skip subviews as they get moved up to replace those removed. In iOS, however, Apple seems to have realized this problem and fixed it by returning a copy automatically. The property declaration only describe how it is set, but apple chose to copy for the getter too.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜