I have a generator which returns objects conforming to protocol A. I would like to add a property, probably with categories, to these objects so i can do something to serve my purposes, which obviously isn't there in the protocol.
is this doable?
I have a generator which returns objects conforming to protocol A. I would like to add a property, probably with categories, to these objects so i can do something to serve my purposes, which obviously isn't there in the protocol.
is this doable?
The more I've thought about this, the more I agree that it's not a duplicate, and in fact the answer is very straightforward as long as what you say you want is what you really want.
Given that you are returning values that conform to some protocol, create a type that conforms to that protocol, and forwards all protocol methods to a wrapped value. Then you can add whatever additional properties you'd like.
For example, given a protocol like:
@protocol Runnable <NSObject>
- (void)run;
@end
You can create a trivial wrapper like:
@interface AnyRunnable: NSObject <Runnable>
- (instancetype)initWithRunnable:(id<Runnable>)runnable;
@end
@interface AnyRunnable (Private)
@property (nonatomic, readwrite) id<Runnable> wrapped;
@end
@implementation AnyRunnable
- (instancetype)initWithRunnable:(id<Runnable>)wrapped
{
self = [super init];
if (self) {
self.wrapped = wrapped;
}
return self;
}
- (void)run {
[self.wrapped run];
}
@end
Now, AnyRunnable
is a Runnable
, so you can return that from your method. It's also a type you control, so you can add any properties you like to it. This scales to any protocol; you just need to implement the required methods.
It's not possible to do this via a category for the reasons given in Why can't categories have instance variables? (If it were possible to add properties based on protocols, but not classes, then you could just define a protocol that matched your class, and bypass that limitation.)
Here is possible approach (based on Objective-C associated objects). Tested & worked.
Assume we have some class, which we cannot touch
@interface SomeClass: NSObject
@end
@implementation SomeClass
@end
Then some new properties can inject in the following way
@interface SomeClass (VirtualProperty)
@property (atomic) NSInteger virtualProperty;
@property (nonatomic, readonly) NSInteger calculableProperty;
@end
static const char *kVirtualPropertyKey = "virtualProperty";
@implementation SomeClass (VirtualProperty)
@dynamic virtualProperty;
- (NSInteger)calculableProperty {
return self.virtualProperty * 2;
}
- (NSInteger)virtualProperty {
return [(NSNumber *)objc_getAssociatedObject(self,
kVirtualPropertyKey) integerValue];
}
- (void)setVirtualProperty:(NSInteger)newValue {
objc_setAssociatedObject(self, kVirtualPropertyKey,
@(newValue), OBJC_ASSOCIATION_RETAIN);
}
@end
Usage:
SomeClass *some = SomeClass.new;
some.virtualProperty = 5;
NSLog(@"Result: %lu", some.calculableProperty);