добавить свойство категории к классу, подтверждающему протокол - PullRequest
0 голосов
/ 08 февраля 2020

У меня есть генератор, который возвращает объекты, соответствующие протоколу А. Я хотел бы добавить свойство, возможно, с категориями, к этим объектам, чтобы я мог сделать что-то для своих целей, чего явно нет в протоколе.

это выполнимо?

Ответы [ 2 ]

0 голосов
/ 10 февраля 2020

Здесь возможен подход (на основе Objective- C связанных объектов). Протестировано и сработало.

Предположим, у нас есть некоторый класс, к которому мы не можем прикоснуться

    @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);
0 голосов
/ 10 февраля 2020

Чем больше я об этом думаю, тем больше я согласен с тем, что это не дубликат, и на самом деле ответ очень прост, если вы говорите, что хотите, то что вы действительно хотите.

Учитывая, что вы возвращаете значения, соответствующие некоторому протоколу, создайте тип, соответствующий этому протоколу, и перенаправьте все методы протокола в упакованное значение. Затем вы можете добавить любые дополнительные свойства, которые захотите.

Например, с учетом протокола вроде:

@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, так что вы можете вернуть это из вашего метода. Это также тип, которым вы управляете, поэтому вы можете добавлять любые свойства, которые вам нравятся. Это масштабируется до любого протокола; вам просто нужно реализовать необходимые методы.

Невозможно сделать это через категорию по причинам, указанным в Почему у категорий не может быть переменных экземпляра? (Если бы было возможно добавьте свойства на основе протоколов, но не классов, тогда вы можете просто определить протокол, который соответствует вашему классу, и обойти это ограничение.)

...