Ваша категория в порядке, но крайне негибкая.Делегированные обратные вызовы почти всегда должны включать хотя бы один параметр (вызывающий объект), и ваш подход не учитывает параметры.Методы делегатов также часто возвращают значения, и этот подход также не допускает этого.
Как заметил Стивен, правильный код не должен использовать performSelector:
, а просто вызывать метод напрямую.Это имеет преимущество проверки во время компиляции на наличие опечаток, особенно если в сочетании с параметром предупреждения «Необъявленный селектор» (GCC_WARN_UNDECLARED_SELECTOR), который я настоятельно рекомендую.
Если типизация является проблемой, тогда решение являетсябатут.Проблема в том, что батуты значительно медленнее, чем просто вызов метода, но они удобны.Например, вот пример того, о чем вы говорите.(Я не проверял это; он урезан по сравнению с более сложным, который я использую для отправки сообщений нескольким делегатам, где это того стоит).
#import <objc/runtime.h>
@interface RNDelegateTrampoline : NSObject {
@private
id delegate_;
Protocol *protocol_;
}
@property (nonatomic, readwrite, assign) id delegate;
@property (nonatomic, readwrite, retain) Protocol *protocol;
- (id)initWithProtocol:(Protocol *)aProtocol delegate:(id)aDelegate;
@end
@implementation RNDelegateTrampoline
- (id)initWithProtocol:(Protocol *)aProtocol delegate:(id)aDelegate {
if ((self = [super init])) {
protocol_ = [aProtocol retain];
delegate_ = aDelegate;
}
return self;
}
- (void)dealloc {
[protocol_ release], protocol_ = nil;
delegate_ = nil;
[super dealloc];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
// Look for a required method
struct objc_method_description desc = protocol_getMethodDescription(self.protocol, selector, YES, YES);
if (desc.name == NULL) {
// Maybe it's optional
desc = protocol_getMethodDescription(self.protocol, selector, NO, YES);
}
if (desc.name == NULL) {
[self doesNotRecognizeSelector:selector]; // Raises NSInvalidArgumentException
return nil;
}
else {
return [NSMethodSignature signatureWithObjCTypes:desc.types];
}
}
- (void)forwardInvocation:(NSInvocation *)invocation {
if ([[self delegate] respondsToSelector:[invocation selector]]) {
[invocation invokeWithTarget:[self delegate]];
}
}
@synthesize delegate = delegate_;
@synthesize protocol = protocol_;
@end
Затем вы использовали бы его следующим образом:
@property (nonatomic, readwrite, retain) id delegateTramp;
self.delegateTramp = [[[RNDelegateTrampoline alloc] initWithProtocol:@protocol(ThisObjectDelegate) delegate:aDelegate] autorelease];
...
[self.delegateTramp thisObject:self didSomethingWith:x];
Обратите внимание, что мы использовали id
вместо RNDelegateTrampoline
в качестве типа батута нашего делегата.Это важно, иначе вы получите предупреждения компилятора обо всем, что вы пытаетесь отправить на него.Объявление его как id
преодолевает это.Конечно, он также отбрасывает предупреждения во время компиляции, если вы передаете неизвестный метод вашему делегату.Вы все равно получите исключение во время выполнения.