How do I cleanly override a property setter?
If I want to define a custom UIView subclass that does something when its bounds are set, how do I override the setter? Overriding setBounds seems dangerous since, if I understand correctly, the getter and setter names are not part of the public interface and could change anytime.
Of course, I use class_copyPropertyList to query the runtime for a list of properties defined in the class, then query it for the setter's name, and finally use class_addMethod to add the method, after first getting a reference to the earlier method to use for calling the original version.
All this seems hacky. Is the开发者_运维问答re a clean way to do what I want, which is guaranteed to not break on future OS versions? Thanks.
You can override a setter/getter without having to poke around the internal state of the class (i.e. ivars) -- just call the super's methods in your overrides:
- (Thing *)thing {
// do your extra stuff here
// ...
return [super thing];
}
- (void)setThing:(Thing *)thing {
// do your extra stuff here
// ...
[super setThing:thing];
}
An alternative that might suit your problem is to use KVO.
Update
Of course, overriding setBounds
might not be necessary. See this question -- layoutSubviews
gets called if the frame changes, and changing the bounds causes the frame size to also be updated. So consider putting your code into layoutSubviews
.
Final update
Ok, here is why Apple is never going to suddenly declare some @property
items as using a non-standard method names (as you fear):
It would break everything in the app store.
Think about it this way: at compile time, any code that accesses a property using dot notation, e.g. the obj.x
syntactic sugar, is converted to a message of the form [obj x]
. Likewise for properties -- they are converted at compile time into regular methods. So, compiled binaries know nothing about dot notation and properties -- they just call regular selectors. So if Apple released an update to iOS that declared some public properties as having non-standard implementation methods, everything in the app store could break. Everything. There is no fault on your behalf in this scenario if your app broke like the rest -- it would be Apple's fault, not yours.
@property(nonatomic) CGRect bounds;
is shorthand for
-(CGRect)bounds;
-(void)setBounds:(CGRect)bounds;
,
view.bounds = rect;
is shorthand for
[view setBounds:rect];
,
CGRect rect = view.bounds;
is shorthand for
CGRect rect = [view bounds];
Dot notation and @property declarations are SYNTACTIC SUGAR. They are for shortening code and convenience. Messages and selectors are always always underneath them, and can always be relied on to be a stable, if not the most stable, part of the interface.
Overriding "setBounds:" is a safe way to do this. "setBounds:" is not explicitly named in the public interface, because it is declared as a @property. However the standard is that the setter with "set"-capitalized property name is always created (unless its readonly).
There is an NSViewBoundsDidChange notification in OSX. That does appear to be the better solution, although fears of overriding the @property methods seem unfounded. I happened on this post with the same question about overriding the accessors, and you have convinced me to prefer notifications.
精彩评论