如果我想定义一个自定义的UIView子类,它在设置其边界时执行某些操作,如何覆盖setter?覆盖setBounds似乎很危险,因为如果我理解正确,getter和setter名称不是公共接口的一部分,可能随时改变。
当然,我使用class_copyPropertyList来查询运行时列表中定义的属性列表,然后查询它的setter名称,最后使用class_addMethod添加方法,首先获取对早期方法的引用用于调用原始版本。
这一切看起来都很糟糕。是否有一种干净的方式来做我想要的,保证在未来的操作系统版本上不会中断?感谢。
答案 0 :(得分:19)
你可以覆盖一个setter / getter,而不必嘲笑类的内部状态(即ivars) - 只需在你的覆盖中调用super的方法:
- (Thing *)thing {
// do your extra stuff here
// ...
return [super thing];
}
- (void)setThing:(Thing *)thing {
// do your extra stuff here
// ...
[super setThing:thing];
}
可能适合您的问题的替代方法是使用KVO。
<强>更新强>
当然,可能没有必要覆盖setBounds
。如果帧发生更改,请参阅this question - layoutSubviews
,并且更改边界会导致帧大小也更新。因此,请考虑将您的代码放入layoutSubviews
。
最终更新
好的,这就是为什么Apple永远不会突然将某些@property
项声明为使用非标准方法名称(如您所愿):
它会破坏应用商店中的所有内容。
以这种方式思考:在编译时,使用点表示法访问属性的任何代码,例如: obj.x
语法糖被转换为[obj x]
形式的消息。同样对于属性 - 它们在编译时转换为常规方法。因此,编译的二进制文件对点符号和属性一无所知 - 它们只调用常规选择器。因此,如果Apple发布了一个更新iOS,它将某些公共属性声明为具有非标准实现方法,那么应用程序商店可能会破一切。如果您的应用程序像其他应用程序一样崩溃,那么在这种情况下您没有任何错误 - 这将是Apple的错,而不是您的错误。
答案 1 :(得分:2)
@property(nonatomic) CGRect bounds;
是
的简写-(CGRect)bounds;
-(void)setBounds:(CGRect)bounds;
,
view.bounds = rect;
是
的简写[view setBounds:rect];
,
CGRect rect = view.bounds;
是
的简写CGRect rect = [view bounds];
点符号和@property声明是SYNTACTIC SUGAR。它们用于缩短代码和方便性。消息和选择器总是位于它们之下,并且始终可以依赖于接口的稳定(如果不是最稳定)部分。
覆盖“setBounds:”是一种安全的方法。 “setBounds:”未在公共接口中显式命名,因为它被声明为@property。但是标准是始终创建具有“set”-capitalized属性名称的setter(除非它只读)。
答案 2 :(得分:0)
OSX中有一个NSViewBoundsDidChange通知。这似乎是更好的解决方案,尽管对覆盖@property方法的担忧似乎没有根据。我在这篇文章中遇到了关于覆盖访问者的相同问题,你说服我更喜欢通知。