有什么方法可以将 Objective-C 类别仅应用于当前 class(或等效效果)?

Any way to apply Objective-C category only to current class (or equivalent effect)?

假设我有一个名为 MyCustomView 的自定义 UIView 子类。假设我在 UIView 上有一个名为 UIView+Dictionary 的类别,它向每个 UIView 添加了一个名为 dictionary 的 NSDictionary 属性。

如果我将 UIView+Dictionary.h 导入 MyCustomView.m,那么 MyCustomView.m 中引用的每个视图都会添加 dictionary 属性,这在很多情况下正是所需的行为。

但是,如果我希望 UIView+Dictionary 仅应用于 MyCustomView 本身而不是 MyCustomView.m 中引用的每个 UIView,有没有办法这样做(或实现类似的效果)?

我想避免使 MyCustomView 成为另一个自定义子类的子类(例如,MyViewWithDictionary),因为理想情况下我希望能够为类似于多重继承(例如,UIView+DictionaryUIView+BorderUIView+CustomAnimations)。

在我自己的实际场景中,我编写了一个类别以在视图控制器中自动实现自定义 UINavigationBar,但我希望该类别仅应用于我正在导入该类别的视图控制器,并且不是该文件中可能引用的任何其他视图控制器。

感谢任何和所有见解!我提前道歉,因为我相当确定上面描述的效果有更多正确的术语。

However, if I wanted UIView+Dictionary applied only to MyCustomView itself [...] is there a way to do so [...]?

只能通过将类别更改为在 MyCustomView 而不是 UIView 上。

header 与类别的方法是否在任何给定实例上存在 无关。如果类别被编译到您的程序中,那么无论在何处创建实例,方法都在那里。这就是前缀对添加到框架的方法如此重要的原因 classes:类别具有全局影响,名称冲突是未定义的行为。

header 仅影响编译器所关注的方法的可见性。无论如何,您都可以使用通常的技巧在运行时调用它们。

类别在 class 本身 上生效,当运行时在启动时被初始化。如果希望类别的方法只在某个class上可用,类别必须定义在那个class.

正如 Josh 所指出的,除非您调用它们,否则在类别中添加的任何方法基本上都是惰性的。我遇到的问题是类别中生成的属性和混合方法(因为,正如 Josh 还指出的那样,Objective-C 中没有混入)。

我能够通过在我的类别中添加默认为 NO 并充当我想要的任何类别方法和属性的 "switch" 的自定义 BOOL 来解决这个问题指定。

例如,如果我希望我的 dictionary 属性 延迟实例化但仅在 MyCustomView 内实例化,我可以执行以下操作:

// UIView+Dictionary.h

@interface UIView (Dictionary)
@property (nonatomic) BOOL enableDictionary;
@property (nonatomic, strong) NSDictionary *dictionary;
@end

// UIView+Dictionary.m

#import "UIViewController+CustomNavigationBar.h"
#import <objc/runtime.h>

@implementation UIView (Dictionary)

- (void)setEnableDictionary:(BOOL)enableDictionary {
    objc_setAssociatedObject(self, @selector(enableDictionary), @(enableDictionary), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (BOOL)enableDictionary {
    NSNumber *enableDictionaryValue = objc_getAssociatedObject(self, @selector(enableDictionary));
    if (enableDictionaryValue) {
        return enableDictionaryValue.boolValue;
    }

    objc_setAssociatedObject(self, @selector(enableDictionary), @NO, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    return self.enableDictionary;
}

- (void)setDictionary:(NSDictionary *)dictionary {
    objc_setAssociatedObject(self, @selector(dictionary), dictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSDictionary *)dictionary {
    if (!self.enableDictionary) {
        return nil;
    }

    NSDictionary *dictionary = objc_getAssociatedObject(self, @selector(dictionary));
    if (dictionary) {
        return dictionary;
    }

    objc_setAssociatedObject(self, @selector(dictionary), @{}, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    return self.dictionary;
}

@end

然后在 -[MyCustomView viewDidLoad] 内我可以简单地调用 self.enableDictionary = YES。这样,只有 MyCustomView 的实例才会有一个非零延迟实例化的 NSDictionary。 (请注意,在此示例中,UIViews 的所有实例仍将响应选择器 @selector(dictionary),但我们的行为将根据 enableDictionaryYES 还是 NO 而有所不同。 )

虽然这是一个微不足道的例子,但相同的策略可用于在类别中调配的方法。 (同样,在类别中混合方法可能是一种不好的形式,但在某些情况下是必要的邪恶。)