最简单的方法是“模拟”实现仅属性协议的对象

时间:2014-02-06 21:46:21

标签: ios objective-c mocking protocols

请原谅我的疯狂需求,但是我正在编写一个框架中创建一堆属性协议。

@protocol SomePropertiesOfAnObjectThing <NSObject>

@property (nonatomic, strong) NSString *larry;
@property (nonatomic, strong) NSString *curly;
@property (nonatomic, strong) NSString *moe;

@end

有没有办法,更重要的是一种简单的方法,“模拟”实现此协议的对象?

id<SomePropertiesOfAnObjectThing> thingy = [ProtocolObject fromProtocol:@protocol(SomePropertiesOfAnObjectThing)];

thingy.larry = @"fizz";
thingy.curly = @"buzz";
thingy.moe = @"bar";

我想避免的两件事:

  1. 明确创建新类
  2. 第三方依赖项(例如OCMock)
  3. 我会为任何可以通过投票给我一个干净简单的方式的人(好吧,至少1个)。

2 个答案:

答案 0 :(得分:0)

关于协议的事情是你只是向编译器承诺某个类将实现协议中定义的方法。您可以创建一个实际提供方法实现的类。协议中定义的属性不是由声明符合协议的类自动合成的,您需要定义底层的ivar,并为协议中定义的每个属性实现setter和getter以完全符合它。

换句话说,thingy只是因为它符合协议而免费获得这些属性。

答案 1 :(得分:0)

我天真的解决方案如下:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    return [[self class] instanceMethodSignatureForSelector:@selector(foo:)];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    NSString *argument;
    [anInvocation getArgument:&argument atIndex:2];
    if (argument) {
        NSString *setterName = NSStringFromSelector(anInvocation.selector);
        NSRange range = NSMakeRange(3, [setterName length]-4);
        NSString *propertyName = [[setterName substringWithRange:range] lowercaseString];
        [self performSelector:@selector(setFoo:value:) withObject:propertyName withObject:argument];
    } else {
        [self performSelector:@selector(foo:) withObject:argument];
    }
}

- (id)foo:(id)key
{
    return self.properties[key];
}

- (void)setFoo:(id)key value:(id)value
{
    self.properties[key] = value;
}

- (id)valueForKey:(NSString *)key
{
    return self.properties[key];
}

- (void)setValue:(id)value forKey:(NSString *)key
{
    self.properties[key] = value;
}

稍后我将使用更清洁的版本进行更新,因为我会优化值类型等。