当调用特定子类中可用的方法时,超类类别中的performSelector失败

时间:2013-05-18 20:27:20

标签: ios objective-c uitextview nsattributedstring objective-c-category

我有UIView类别,它定义了操纵UILabel和UITextView的attributedText属性的方法。

@implementation UIView (replaceAttrText)
-(void) replaceAttrText: (NSString *)str {
    if ([self respondsToSelector: @selector(setAttributedText)]) {
        NSMutableAttributedString *labelText = [self template];

        // change it

        [self performSelector:@selector(setAttributedText) withObject: labelText];
    }
}
@end

对于UILabel和UITextView,respondsToSelector都返回false(虽然它们响应setAttributedText),如果直接执行setAttributedText而没有respondsToSelector,则会引发异常。

当直接在UILabel上实现类别(没有选择器)时一切正常,但遗憾的是UILabel和UITextView没有具有属性的共同祖先。

我做错了什么?谢谢!

2 个答案:

答案 0 :(得分:5)

UILabelUITextView没有名为setAttributedText的方法。但他们确实有一个名为setAttributedText:的方法。注意冒号。冒号是方法名称的一部分。拥有与否代表两种完全不同的方法。

将您的代码更改为:

-(void) replaceAttrText: (NSString *)str {
    if ([self respondsToSelector: @selector(setAttributedText:)]) {
        NSMutableAttributedString *labelText = [self template];

        // change it

        [self performSelector:@selector(setAttributedText:) withObject: labelText];
    }
}

换句话说,在setAttributedText的两个引用中添加一个冒号。

答案 1 :(得分:3)

@maddy有正确的具体答案。这解决了被认为是反模式的问题。

通常,您不应使用类别扩展Apple提供的类。这样做意味着您的代码与系统框架有效地交织在一起。这使得维护和增强变得更加困难,因为您最终不仅要处理自己的类,还要重构您的代码 - 通过交织 - 具有与其扩展的类的模式相同的实现以及使用它的类。

这就是为什么Apple通常建议不要使用这些模式。如果你扩展Apple类,你永远不应该覆盖现有方法(打破实现细节),你应该总是为你的方法添加一个前缀,以便操作系统的未来版本 - - 甚至更新 - 不要碰巧包含一个与你的方法发生冲突的方法(之前发生过addObjectIfAbsent: NSMutableArray是最值得注意的此类事件。)

同样,检查isKindOfClass:respondsToSelector:的有时做某事的行为有时也不行为是另一种反模式。一个设计良好的应用程序通常应该避免传递类型如此通用,以至于该类型的接收器必须在它可以对其进行操作之前弄清楚它是什么。除此之外,这样做会使编译器无法再次检查代码的正确性。

我建议你重构你的应用程序,这样你需要通过一种方式访问​​需要归属文本的UI对象,而不是通过另一种方式访问​​。即无论调用replaceAttrText:(同样,在Objective-C中,很少使用缩写.IDE的完成使您很少需要输入任何内容而缺少缩写导致代码清晰度)只会在对象上执行此操作。确实需要调整他们的归属文本。如果您动态或以编程方式生成用户界面,则可能有一个位于模型和处理此用户界面的视图之间的控制器对象。