Objective-C是否具有协议消息转发的运行时方法?

时间:2015-11-13 11:34:36

标签: objective-c cocoa protocols

我经常使用这些协议:

@protocol AnotherObjectDelegate <NSObject>
-(void)someMethodWithObject:(id)object;
@end
@interface AnotherObject : NSObject
@property (assign) id<AnotherObjectDelegate> delegate;
@end

My BaseObject符合该协议,也是接收来自AnotherObject的消息的委托。

@interface BaseObject : NSObject <AnotherObjectDelegate>
@property AnotherObject* anotherObject;
@property SecondLevelObject* secondLevelObject;
@end

@implementation BaseObject
-(instancetype)init {
    if (self = [super init]) {
        self.anotherObject = [AnotherObject new];
        self.anotherObject.delegate = self;
    }
}
-(void)someMethodWithObject:(id)object {
    // Forwarding Message from Delegate to other Object conforming to protocol:
    [self.secondLevelObject someMethodWithObject:object];
}
@end

然而,BaseObject-Instance作为其他实例的某种代理,用于将协议消息转发给BaseObject之后由object拥有的其他对象:

@interface SecondLevelObject : NSObject <AnotherObjectDelegate>
@property ThirdLevelObject* thirdLevelObject;
@end

@implementation SecondLevelObject
-(void)someMethodWithObject:(id)object {
    [self.thirdLevelObject someMethodWithObject:object];
}
@end

在SecondLevelObject中有一个ThirdLevelObject,它也符合相同的协议。在这里我也转发了消息 - 所以SecondLevelObjects也可以作为某种代理。

@interface ThirdLevelObject : NSObject <AnotherObjectDelegate>
@end

@implementation ThirdLevelObject
-(void)someMethodWithObject:(id)object {
    // Finally it's here
}
@end

我正在使用这种类设计,以防止使用长指针链,如:

anotherObject.delegate = baseObject.secondLevelObject.thirdLevelObject;

并防止我的类需要很多弱/赋值引用,当你有多个符合多种协议的对象时,这些引用很难调试。

其中一个缺点是我必须在每个类中添加类似样板代码的协议实现,这些类被用作&#34;代理&#34;转发消息。即使 - 在我的情况下 - 这更容易阅读和调试。

所以我问自己是否会有更简单的方法。我也是这样做的,以防止我的代码调用

if ([delegate respondsToSelector:@selector(someMethod)]
    [delegate someMethod];

是否有某种Objective-C运行时功能可以帮助我?

1 个答案:

答案 0 :(得分:1)

最简单的方法是实施forwardingTargetForSelector:。当您收到不响应的消息时,将调用此方法,并且将返回它返回的任何对象。

例如:

@implementation SecondLevelObject
- (id)forwardingTargetForSelector:(SEL)aSelector {
    // Often you would actually check the selector here
    return self.thirdLevelObject;
}
@end

问题是现在SecondLevelObject似乎不符合协议,并且会产生警告。您可以使用编译指示来抑制该警告(因为您确实符合协议):

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wprotocol"
@implementation SecondLevelObject
...
@end
#pragma clang diagnostic pop

这会关闭整个对象的协议检查,所以你需要非常小心。在大多数情况下,我发现编写转发代码更容易,更清晰。如果有很多,它有时表示不同的设计问题。也许LevelThreeObject应该真的是委托本身,或者像通知或KVO这样的松散系统会更好。但转发确实仍然非常合理,这是一种需要考虑的技术。