Let's say I have a custom subclass of UIView called MyCustomView
. Let's also say that I have a category on UIView called UIView+Dictionary
that adds an NSDictionary property called dictionary
to every UIView.
If I were to import UIView+Dictionary.h
into MyCustomView.m
then every view referenced within MyCustomView.m
would have this added dictionary
property, which in many situations is exactly the desired behavior.
However, if I wanted UIView+Dictionary
applied only to MyCustomView
itself and not to every UIView referenced within MyCustomView.m
, is there a way to do so (or achieve a similar effect)?
I'd like to avoid making MyCustomView
a subclass of another custom subclass (e.g., MyViewWithDictionary
), as I'd ideally like to be able to import multiple categories for something akin to multiple inheritance (e.g., UIView+Dictionary
, UIView+Border
, UIView+CustomAnimations
).
In my actual own scenario, I've written a category to automatically implement a custom UINavigationBar in a view controller, but I'd like that category to apply only to the view controller into which I am importing the category and not any other view controllers that may be referenced in that file.
Any and all insights are appreciated! And I apologize in advance as I am fairly certain there are more correct terminologies for the effect described above.
As Josh pointed out, any methods added in categories are basically inert unless you call them. The issue that I was having was for generated properties and swizzled methods in categories (since, as Josh also pointed out, there are no mixins in Objective-C).
I was able to solve this by adding in a custom BOOL
in my category that defaults to NO
and acts as a "switch" for whatever category methods and properties I want to specify.
E.g., if I wanted my dictionary
property to be lazily instantiated but only within MyCustomView
, I could do the following:
// 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
And then within -[MyCustomView viewDidLoad]
I could simply call self.enableDictionary = YES
. That way, only instances of MyCustomView will have a non-nil lazily instantiated NSDictionary. (Note that, in this example, all instances of UIViews will still respond to the selector @selector(dictionary)
, but our behavior will differ based on whether enableDictionary
is YES
or NO
.)
While that is a trivial example, the same strategy can be used for methods that are swizzled within categories. (Again, swizzling methods within categories is probably bad form but a necessary evil in certain scenarios.)