0

我有 UIView 类别,它定义了操作attributedTextUILabel 和 UITextView 属性的方法。

@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

respondsToSelector 为 UILabel 和 UITextView 返回 false(尽管它们响应 setAttributedText),如果 setAttributedText 直接执行而不respondsToSelector检查,则会引发异常。

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

我究竟做错了什么?谢谢!

4

2 回答 2

5

UILabel并且UITextView没有名为setAttributedText. 但他们确实有一个名为setAttributedText:. 注意冒号。冒号是方法名称的一部分。拥有与否代表两种完全独立的方法。

将您的代码更改为:

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

        // change it

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

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

于 2013-05-18T20:31:17.220 回答
3

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

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

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

同样,有时做的工作有时不做检查的行为isKindOfClass:或者respondsToSelector:是另一种反模式。一个架构良好的应用程序通常应该避免传递如此通用的类型,以至于类型的接收者必须在对其进行操作之前弄清楚它是什么。这样做会破坏编译器仔细检查代码正确性等的能力。

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

于 2013-05-19T18:08:49.250 回答