Best way check if delegate responds to optional selector
我已经使用包装将
1 2 3 | if ([self.delegate respondsToSelector:@selector(method:)]) { [self.delegate method:obj]; } |
它工作得很好,但是如果有很多委托的方法,它看起来像是复制
在某个时候,我把
1 2 3 4 5 6 7 8 9 | //ignore warning #pragma clang diagnostic ignored"-Warc-performSelector-leaks" - (void)performDelegateSelector:(SEL)selector withObject:(id)obj { if ([self.delegate respondsToSelector:selector]) { [self.delegate performSelector:selector withObject:obj]; } } |
结果,我只有一个选择:检查,但它看起来仍然没有得到很好的改善。
1 | [self performDelegateSelector:@selector(method:) withObject:self]; |
你怎么认为?使用一些助手或类别包装发送所有
性能方面,这取决于您对委托的使用,它是否直接影响到UI(在这种情况下,您希望有更快的响应时间)等。
一个好的速度优化是在第一次设置委托时将委托实现的所有方法注册到数据结构中。你可以这样做:
1 2 3 4 5 6 7 8 9 10 11 | struct { unsigned int myFirstMethod:1; unsigned int mySecondMethod:1; } delegateRespondsTo; -(void)setDelegate:(id<MyProtocol>)delegate { self.delegate = delegate; delegateRespondsTo.myFirstMethod = [delegate respondsToSelector:@selector(myFirstMethod)]; delegateRespondsTo.mySecondMethod = [delegate respondsToSelector:@selector(mySecondMethod)]; } |
那你就可以简单地
1 2 | if ( delegateRespondsTo.myFirstMethod ) [self.delegate myFirstMethod]; |
检查这个很好的答案以获得更详尽的解释。
你可以用蹦床来做这个。蹦床是将消息转发到另一个对象的对象。这是一个简单的Delegate蹦床。
1 2 3 4 5 | #import <objc/runtime.h> @interface DelegateTrampoline : NSObject - (id)initWithProtocol:(Protocol *)protocol delegate:(id)delegate; @end |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | #import"DelegateTrampoline.h" @interface DelegateTrampoline () @property (nonatomic) Protocol *protocol; @property (nonatomic, weak) id delegate; @end @implementation DelegateTrampoline - (id)initWithProtocol:(Protocol *)protocol delegate:(id)delegate { self = [super init]; if (self) { _protocol = protocol; _delegate = delegate; } return self; } - (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 { SEL selector = [invocation selector]; if ([self.delegate respondsToSelector:selector]) { [invocation setTarget:self.delegate]; [invocation invoke]; } } @end |
以下是您将如何使用它:
1 2 3 4 5 6 7 8 9 | @interface MyObject () @property (nonatomic) id delegateTrampoline; @end ... self.delegateTrampoline = [[DelegateTrampoline alloc] initWithProtocol:@protocol(MyProtocol) delegate:delegate]; [self.delegateTrampoline myobject:self didSomethingAtIndex:1]; @end |
一些注释:
- 与其他方法相比,这是非常缓慢的。它需要创建一个
NSInvocation 对象,比简单的方法调用慢数百倍。也就是说,除非在一个紧密的循环中调用委托方法,否则这可能不是问题。 - 您必须声明
delegateTrampoline 为id 类型。这允许您将任意选择器传递给它。但这也意味着编译器无法检测是否将选择器传递给了不在协议中的delegateTrampoline 。如果你这样做,你会在运行时崩溃。编译器可以检测您是否传递了一个完全未知的选择器,因此它将捕获简单的拼写错误。
除@micantox提供的答案外,我还想补充一下,您可以使用
1 2 3 4 5 6 | typedef NS_OPTIONS (NSUInteger, MyDelegateOptions) { MyDelegateOptionDidFinishedWithTaskOne = 1 << 0, MyDelegateOptionDidFinishedWithTaskTwo = 1 << 1, MyDelegateOptionDidFinishedWithTaskThree = 1 << 2 }; |
然后您可以创建可以保存
检查一下你的设定器中是否有代表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | - (void)setDelegate:(id<MyDelegateProtocol>)delegate { _delegate = delegate; if ([delegate respondsToSelector:@selector(myDelegateDidFinishedWithTaskOne:)]) { { self.delegateOptions = self.delegateOptions | MyDelegateOptionDidFinishedWithTaskOne; } if ([delegate respondsToSelector:@selector(myDelegateDidFinishedWithTaskTwo:)]) { { self.delegateOptions = self.delegateOptions | MyDelegateOptionDidFinishedWithTaskTwo; } if ([delegate respondsToSelector:@selector(myDelegateDidFinishedWithTaskThree:)]) { { self.delegateOptions = self.delegateOptions | MyDelegateOptionDidFinishedWithTaskThree; } } |
然后您可以使用
1 2 3 | if (self.delegateOptions & MyDelegateOptionDidFinishedWithTaskOne) { [self.delegate myDelegateDidFinishedWithTaskTwo:myResult]; } |
如您所见,如果EDOCX1的位(13)设置为1,即使其他位也设置为1,那么使用
因此,如果您假设
00000111 & 00000001 = 00000001
你可以用你的条件声明得到
当你只需要检查一次或两次你的