如何干净地覆盖属性设置器?

时间:2011-03-21 15:24:45

标签: iphone cocoa cocoa-touch ios uikit

如果我想定义一个自定义的UIView子类,它在设置其边界时执行某些操作,如何覆盖setter?覆盖setBounds似乎很危险,因为如果我理解正确,getter和setter名称不是公共接口的一部分,可能随时改变。

当然,我使用class_copyPropertyList来查询运行时列表中定义的属性列表,然后查询它的setter名称,最后使用class_addMethod添加方法,首先获取对早期方法的引用用于调用原始版本。

这一切看起来都很糟糕。是否有一种干净的方式来做我想要的,保证在未来的操作系统版本上不会中断?感谢。

3 个答案:

答案 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方法的担忧似乎没有根据。我在这篇文章中遇到了关于覆盖访问者的相同问题,你说服我更喜欢通知。