From 55dac752013024bdec47290d9e1d6bb2f63650b1 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sat, 5 Dec 2015 12:04:51 +0100 Subject: [PATCH] Fixes problem with improper detection of delegate proxy class hierarchies. #286 --- RxCocoa/Common/_RXDelegateProxy.m | 54 ++++++++++++------- .../Tests/DelegateProxyTest.swift | 9 +++- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/RxCocoa/Common/_RXDelegateProxy.m b/RxCocoa/Common/_RXDelegateProxy.m index a3b14c7e..f77bdb80 100644 --- a/RxCocoa/Common/_RXDelegateProxy.m +++ b/RxCocoa/Common/_RXDelegateProxy.m @@ -106,31 +106,46 @@ static NSMutableDictionary *forwardableSelectorsPerClass = nil; forwardableSelectorsPerClass = [[NSMutableDictionary alloc] init]; } - // NSLog(@"Class: %@", NSStringFromClass(self)); - NSMutableSet *allowedSelectors = [NSMutableSet set]; - - unsigned int count; - Protocol *__unsafe_unretained *pProtocols = class_copyProtocolList(self, &count); - - for (unsigned int i = 0; i < count; i++) { + + +#define CLASS_HIERARCHY_MAX_DEPTH 100 + + NSInteger classHierarchyDepth = 0; + Class targetClass = self; + + for (classHierarchyDepth = 0, targetClass = self; + classHierarchyDepth < CLASS_HIERARCHY_MAX_DEPTH && targetClass != nil; + ++classHierarchyDepth, targetClass = class_getSuperclass(targetClass) + ) { + unsigned int count; + Protocol *__unsafe_unretained *pProtocols = class_copyProtocolList(targetClass, &count); - unsigned int protocolMethodCount = 0; - Protocol *protocol = pProtocols[i]; - struct objc_method_description *methods = protocol_copyMethodDescriptionList(protocol, NO, YES, &protocolMethodCount); - - for (unsigned int j = 0; j < protocolMethodCount; ++j) { - struct objc_method_description method = methods[j]; - if (RX_is_method_with_description_void(method)) { - // NSLog(@"Allowed selector: %@", NSStringFromSelector(method.name)); - [allowedSelectors addObject:SEL_VALUE(method.name)]; + for (unsigned int i = 0; i < count; i++) { + + unsigned int protocolMethodCount = 0; + Protocol *protocol = pProtocols[i]; + struct objc_method_description *methods = protocol_copyMethodDescriptionList(protocol, NO, YES, &protocolMethodCount); + + for (unsigned int j = 0; j < protocolMethodCount; ++j) { + struct objc_method_description method = methods[j]; + if (RX_is_method_with_description_void(method)) { + [allowedSelectors addObject:SEL_VALUE(method.name)]; + } } + + free(methods); } - free(methods); + free(pProtocols); + } + + if (classHierarchyDepth == CLASS_HIERARCHY_MAX_DEPTH) { + NSLog(@"Detected weird class hierarchy with depth over %d. Starting with this class -> %@", CLASS_HIERARCHY_MAX_DEPTH, self); +#if DEBUG + abort(); +#endif } - - free(pProtocols); forwardableSelectorsPerClass[CLASS_VALUE(self)] = allowedSelectors; } @@ -174,7 +189,6 @@ static NSMutableDictionary *forwardableSelectorsPerClass = nil; [self interceptedSelector:anInvocation.selector withArguments:arguments]; } - //NSLog(@"Sent selector %@", NSStringFromSelector(anInvocation.selector)); if (self._forwardToDelegate && [self._forwardToDelegate respondsToSelector:anInvocation.selector]) { [anInvocation invokeWithTarget:self._forwardToDelegate]; } diff --git a/RxTests/RxSwiftTests/Tests/DelegateProxyTest.swift b/RxTests/RxSwiftTests/Tests/DelegateProxyTest.swift index 358ff480..59285b00 100644 --- a/RxTests/RxSwiftTests/Tests/DelegateProxyTest.swift +++ b/RxTests/RxSwiftTests/Tests/DelegateProxyTest.swift @@ -253,4 +253,11 @@ class DelegateProxyTest : RxTest { view = nil XCTAssertTrue(completed.value) } -} \ No newline at end of file +} + +extension DelegateProxyTest { + func test_DelegateProxyHierarchyWorks() { + let tableView = UITableView() + _ = tableView.rx_delegate.observe("scrollViewWillBeginDragging:") + } +}