我有一个生成器,该生成器返回符合协议A的对象。我想为这些对象添加一个属性(可能带有类别),以便我可以做一些事来达到自己的目的,这显然不在协议中。
这可行吗?
鉴于您要返回符合某种协议的值,请创建符合该协议的类型,并将所有协议方法转发给包装的值。然后,您可以添加所需的任何其他属性。
例如,给定类似协议:
@protocol Runnable <NSObject>
- (void)run;
@end
您可以创建一个简单的包装,例如:
@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
现在,AnyRunnable
是Runnable
,因此您可以从方法中返回它。这也是您控制的类型,因此您可以向其添加任何所需的属性。这可以扩展到任何协议。您只需要实现所需的方法即可。出于Why can't categories have instance variables?中给出的原因,无法通过类别执行此操作(如果可以基于协议(而非类)添加属性,则可以定义与您的类匹配的协议,然后绕过该协议限制。)
假设我们有一些我们无法接触的课程
@interface SomeClass: NSObject
@end
@implementation SomeClass
@end
然后可以通过以下方式注入一些新属性
@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
用法:
SomeClass *some = SomeClass.new; some.virtualProperty = 5; NSLog(@"Result: %lu", some.calculableProperty);