Objective-C 是否具有协议消息转发的运行时方法?
Does Objective-C feature runtime methods for Protocol message forwarding?
我经常像这样使用协议:
@protocol AnotherObjectDelegate <NSObject>
-(void)someMethodWithObject:(id)object;
@end
@interface AnotherObject : NSObject
@property (assign) id<AnotherObjectDelegate> delegate;
@end
我的 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 之后的对象所拥有的其他对象:
@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
我正在使用这种 class 设计,以防止使用像这样的长指针链:
anotherObject.delegate = baseObject.secondLevelObject.thirdLevelObject;
并防止我的 classes 有太多 weak/assign-references 当你有多个对象符合多个协议时,这可能很难调试。
缺点之一是我必须在每个 class 中添加类似样板代码的协议实现,这些 class 被用作 "proxy" 来转发消息。即使 - 就我而言 - 这更容易阅读和调试。
所以我问自己是否有更简单的方法来做到这一点。我也这样做是为了防止我的代码调用
if ([delegate respondsToSelector:@selector(someMethod)]
[delegate someMethod];
是否有某种 Objective-C 运行时函数可以帮助我?
最简单的是实现forwardingTargetForSelector:
。当您收到您不回复的消息时,将调用此方法,并且将向它 returns 的任何对象发送消息。
例如:
@implementation SecondLevelObject
- (id)forwardingTargetForSelector:(SEL)aSelector {
// Often you would actually check the selector here
return self.thirdLevelObject;
}
@end
问题是现在 SecondLevelObject
似乎不符合协议,这会产生警告。您可以使用 pragma 来抑制该警告(因为您确实遵守协议):
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wprotocol"
@implementation SecondLevelObject
...
@end
#pragma clang diagnostic pop
不过,这会关闭整个对象的协议检查,因此您需要非常小心。在大多数情况下,我发现只编写转发代码会更容易、更清晰。如果有很多,它有时表示不同的设计问题。也许 LevelThreeObject 真的应该是委托本身,或者像通知或 KVO 这样更宽松的系统会更好。但是转发仍然很合理,这是一种需要考虑的技术。
我经常像这样使用协议:
@protocol AnotherObjectDelegate <NSObject>
-(void)someMethodWithObject:(id)object;
@end
@interface AnotherObject : NSObject
@property (assign) id<AnotherObjectDelegate> delegate;
@end
我的 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 之后的对象所拥有的其他对象:
@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
我正在使用这种 class 设计,以防止使用像这样的长指针链:
anotherObject.delegate = baseObject.secondLevelObject.thirdLevelObject;
并防止我的 classes 有太多 weak/assign-references 当你有多个对象符合多个协议时,这可能很难调试。
缺点之一是我必须在每个 class 中添加类似样板代码的协议实现,这些 class 被用作 "proxy" 来转发消息。即使 - 就我而言 - 这更容易阅读和调试。
所以我问自己是否有更简单的方法来做到这一点。我也这样做是为了防止我的代码调用
if ([delegate respondsToSelector:@selector(someMethod)]
[delegate someMethod];
是否有某种 Objective-C 运行时函数可以帮助我?
最简单的是实现forwardingTargetForSelector:
。当您收到您不回复的消息时,将调用此方法,并且将向它 returns 的任何对象发送消息。
例如:
@implementation SecondLevelObject
- (id)forwardingTargetForSelector:(SEL)aSelector {
// Often you would actually check the selector here
return self.thirdLevelObject;
}
@end
问题是现在 SecondLevelObject
似乎不符合协议,这会产生警告。您可以使用 pragma 来抑制该警告(因为您确实遵守协议):
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wprotocol"
@implementation SecondLevelObject
...
@end
#pragma clang diagnostic pop
不过,这会关闭整个对象的协议检查,因此您需要非常小心。在大多数情况下,我发现只编写转发代码会更容易、更清晰。如果有很多,它有时表示不同的设计问题。也许 LevelThreeObject 真的应该是委托本身,或者像通知或 KVO 这样更宽松的系统会更好。但是转发仍然很合理,这是一种需要考虑的技术。