diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index b0fd1398..5a698a1a 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -222,10 +222,8 @@ C8093EE61B8A732E0088E94D /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E8D1B8A732E0088E94D /* Logging.swift */; }; C8093EE71B8A732E0088E94D /* ControlTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E901B8A732E0088E94D /* ControlTarget.swift */; }; C8093EE81B8A732E0088E94D /* ControlTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E901B8A732E0088E94D /* ControlTarget.swift */; }; - C8093EE91B8A732E0088E94D /* Deallocating.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E911B8A732E0088E94D /* Deallocating.swift */; }; - C8093EEA1B8A732E0088E94D /* Deallocating.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E911B8A732E0088E94D /* Deallocating.swift */; }; - C8093EEB1B8A732E0088E94D /* DeinitAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E921B8A732E0088E94D /* DeinitAction.swift */; }; - C8093EEC1B8A732E0088E94D /* DeinitAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E921B8A732E0088E94D /* DeinitAction.swift */; }; + C8093EE91B8A732E0088E94D /* MessageSentObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E911B8A732E0088E94D /* MessageSentObserver.swift */; }; + C8093EEA1B8A732E0088E94D /* MessageSentObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E911B8A732E0088E94D /* MessageSentObserver.swift */; }; C8093EED1B8A732E0088E94D /* KVOObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E931B8A732E0088E94D /* KVOObservable.swift */; }; C8093EEE1B8A732E0088E94D /* KVOObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E931B8A732E0088E94D /* KVOObservable.swift */; }; C8093EEF1B8A732E0088E94D /* KVOObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E941B8A732E0088E94D /* KVOObserver.swift */; }; @@ -554,7 +552,6 @@ C8F0C0211BBBFBB9001B112F /* KVOObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E931B8A732E0088E94D /* KVOObservable.swift */; }; C8F0C0221BBBFBB9001B112F /* UIButton+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254061B8A752B00B02D69 /* UIButton+Rx.swift */; }; C8F0C0231BBBFBB9001B112F /* CLLocationManager+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E8A1B8A732E0088E94D /* CLLocationManager+Rx.swift */; }; - C8F0C0241BBBFBB9001B112F /* DeinitAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E921B8A732E0088E94D /* DeinitAction.swift */; }; C8F0C0251BBBFBB9001B112F /* RxActionSheetDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88253FA1B8A752B00B02D69 /* RxActionSheetDelegateProxy.swift */; }; C8F0C0261BBBFBB9001B112F /* _RXDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = C8093E851B8A732E0088E94D /* _RXDelegateProxy.m */; }; C8F0C0271BBBFBB9001B112F /* NSObject+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E971B8A732E0088E94D /* NSObject+Rx.swift */; }; @@ -565,7 +562,7 @@ C8F0C02C1BBBFBB9001B112F /* UIDatePicker+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254091B8A752B00B02D69 /* UIDatePicker+Rx.swift */; }; C8F0C02D1BBBFBB9001B112F /* RxTableViewDataSourceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254001B8A752B00B02D69 /* RxTableViewDataSourceProxy.swift */; }; C8F0C02E1BBBFBB9001B112F /* _RXSwizzling.m in Sources */ = {isa = PBXBuildFile; fileRef = C8093E891B8A732E0088E94D /* _RXSwizzling.m */; }; - C8F0C02F1BBBFBB9001B112F /* Deallocating.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E911B8A732E0088E94D /* Deallocating.swift */; }; + C8F0C02F1BBBFBB9001B112F /* MessageSentObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E911B8A732E0088E94D /* MessageSentObserver.swift */; }; C8F0C0301BBBFBB9001B112F /* UIGestureRecognizer+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C882540A1B8A752B00B02D69 /* UIGestureRecognizer+Rx.swift */; }; C8F0C0311BBBFBB9001B112F /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E8B1B8A732E0088E94D /* DelegateProxy.swift */; }; C8F0C0321BBBFBB9001B112F /* RxCLLocationManagerDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E9A1B8A732E0088E94D /* RxCLLocationManagerDelegateProxy.swift */; }; @@ -683,8 +680,7 @@ D2138C8C1BB9BECA00339B5C /* ControlEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D33931B922FB00014629D /* ControlEvent.swift */; }; D2138C8D1BB9BECD00339B5C /* ControlProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D33941B922FB00014629D /* ControlProperty.swift */; }; D2138C8E1BB9BED600339B5C /* ControlTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E901B8A732E0088E94D /* ControlTarget.swift */; }; - D2138C8F1BB9BED600339B5C /* Deallocating.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E911B8A732E0088E94D /* Deallocating.swift */; }; - D2138C901BB9BED600339B5C /* DeinitAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E921B8A732E0088E94D /* DeinitAction.swift */; }; + D2138C8F1BB9BED600339B5C /* MessageSentObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E911B8A732E0088E94D /* MessageSentObserver.swift */; }; D2138C911BB9BED600339B5C /* KVOObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E931B8A732E0088E94D /* KVOObservable.swift */; }; D2138C921BB9BED600339B5C /* KVOObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E941B8A732E0088E94D /* KVOObserver.swift */; }; D2138C931BB9BEDA00339B5C /* NSNotificationCenter+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E951B8A732E0088E94D /* NSNotificationCenter+Rx.swift */; }; @@ -991,8 +987,7 @@ C8093E8C1B8A732E0088E94D /* DelegateProxyType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxyType.swift; sourceTree = ""; }; C8093E8D1B8A732E0088E94D /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; C8093E901B8A732E0088E94D /* ControlTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlTarget.swift; sourceTree = ""; }; - C8093E911B8A732E0088E94D /* Deallocating.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deallocating.swift; sourceTree = ""; }; - C8093E921B8A732E0088E94D /* DeinitAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeinitAction.swift; sourceTree = ""; }; + C8093E911B8A732E0088E94D /* MessageSentObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageSentObserver.swift; sourceTree = ""; }; C8093E931B8A732E0088E94D /* KVOObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVOObservable.swift; sourceTree = ""; }; C8093E941B8A732E0088E94D /* KVOObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVOObserver.swift; sourceTree = ""; }; C8093E951B8A732E0088E94D /* NSNotificationCenter+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSNotificationCenter+Rx.swift"; sourceTree = ""; }; @@ -1466,8 +1461,7 @@ isa = PBXGroup; children = ( C8093E901B8A732E0088E94D /* ControlTarget.swift */, - C8093E911B8A732E0088E94D /* Deallocating.swift */, - C8093E921B8A732E0088E94D /* DeinitAction.swift */, + C8093E911B8A732E0088E94D /* MessageSentObserver.swift */, C8093E931B8A732E0088E94D /* KVOObservable.swift */, C8093E941B8A732E0088E94D /* KVOObserver.swift */, ); @@ -2151,7 +2145,6 @@ C88254281B8A752B00B02D69 /* UIButton+Rx.swift in Sources */, C8093EDF1B8A732E0088E94D /* CLLocationManager+Rx.swift in Sources */, C80DDEA31BCE69BA006A1832 /* Driver.swift in Sources */, - C8093EEB1B8A732E0088E94D /* DeinitAction.swift in Sources */, C882541C1B8A752B00B02D69 /* RxActionSheetDelegateProxy.swift in Sources */, C8093ED51B8A732E0088E94D /* _RXDelegateProxy.m in Sources */, C8093EF51B8A732E0088E94D /* NSObject+Rx.swift in Sources */, @@ -2162,7 +2155,7 @@ C882542B1B8A752B00B02D69 /* UIDatePicker+Rx.swift in Sources */, C88254221B8A752B00B02D69 /* RxTableViewDataSourceProxy.swift in Sources */, C8093EDD1B8A732E0088E94D /* _RXSwizzling.m in Sources */, - C8093EE91B8A732E0088E94D /* Deallocating.swift in Sources */, + C8093EE91B8A732E0088E94D /* MessageSentObserver.swift in Sources */, C882542C1B8A752B00B02D69 /* UIGestureRecognizer+Rx.swift in Sources */, C8093EE11B8A732E0088E94D /* DelegateProxy.swift in Sources */, C8093EF91B8A732E0088E94D /* RxCLLocationManagerDelegateProxy.swift in Sources */, @@ -2206,7 +2199,6 @@ C8093EEE1B8A732E0088E94D /* KVOObservable.swift in Sources */, C8F6A1461BF0B9B2007DF367 /* NSObject+Rx+RawRepresentable.swift in Sources */, C8093EE01B8A732E0088E94D /* CLLocationManager+Rx.swift in Sources */, - C8093EEC1B8A732E0088E94D /* DeinitAction.swift in Sources */, C8DB96841BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift in Sources */, C8093F461B8A732E0088E94D /* NSButton+Rx.swift in Sources */, C80DDEA01BCE69BA006A1832 /* Driver+Subscription.swift in Sources */, @@ -2216,7 +2208,7 @@ C8093EDE1B8A732E0088E94D /* _RXSwizzling.m in Sources */, C80DDE941BCE69BA006A1832 /* ControlEvent+Driver.swift in Sources */, C80DDEB21BCE8CA3006A1832 /* Driver+Operators+arity.swift in Sources */, - C8093EEA1B8A732E0088E94D /* Deallocating.swift in Sources */, + C8093EEA1B8A732E0088E94D /* MessageSentObserver.swift in Sources */, C8093EE21B8A732E0088E94D /* DelegateProxy.swift in Sources */, C8093EFA1B8A732E0088E94D /* RxCLLocationManagerDelegateProxy.swift in Sources */, C8093EE61B8A732E0088E94D /* Logging.swift in Sources */, @@ -2712,7 +2704,6 @@ C8F0C0221BBBFBB9001B112F /* UIButton+Rx.swift in Sources */, C8F0C0231BBBFBB9001B112F /* CLLocationManager+Rx.swift in Sources */, C80DDEA61BCE69BA006A1832 /* Driver.swift in Sources */, - C8F0C0241BBBFBB9001B112F /* DeinitAction.swift in Sources */, C8F0C0251BBBFBB9001B112F /* RxActionSheetDelegateProxy.swift in Sources */, C8F0C0261BBBFBB9001B112F /* _RXDelegateProxy.m in Sources */, C8F0C0271BBBFBB9001B112F /* NSObject+Rx.swift in Sources */, @@ -2723,7 +2714,7 @@ C8F0C02C1BBBFBB9001B112F /* UIDatePicker+Rx.swift in Sources */, C8F0C02D1BBBFBB9001B112F /* RxTableViewDataSourceProxy.swift in Sources */, C8F0C02E1BBBFBB9001B112F /* _RXSwizzling.m in Sources */, - C8F0C02F1BBBFBB9001B112F /* Deallocating.swift in Sources */, + C8F0C02F1BBBFBB9001B112F /* MessageSentObserver.swift in Sources */, C8F0C0301BBBFBB9001B112F /* UIGestureRecognizer+Rx.swift in Sources */, C8F0C0311BBBFBB9001B112F /* DelegateProxy.swift in Sources */, C8F0C0321BBBFBB9001B112F /* RxCLLocationManagerDelegateProxy.swift in Sources */, @@ -2798,7 +2789,7 @@ D203C5111BB9C53E00D02D00 /* UITableView+Rx.swift in Sources */, C80DDEA51BCE69BA006A1832 /* Driver.swift in Sources */, D203C4F81BB9C53700D02D00 /* RxActionSheetDelegateProxy.swift in Sources */, - D2138C8F1BB9BED600339B5C /* Deallocating.swift in Sources */, + D2138C8F1BB9BED600339B5C /* MessageSentObserver.swift in Sources */, D2138C961BB9BEDA00339B5C /* NSURLSession+Rx.swift in Sources */, D203C5051BB9C53E00D02D00 /* UICollectionView+Rx.swift in Sources */, D2138C8D1BB9BECD00339B5C /* ControlProperty.swift in Sources */, @@ -2807,7 +2798,6 @@ D203C50D1BB9C53E00D02D00 /* UISegmentedControl+Rx.swift in Sources */, D2138C861BB9BEBE00339B5C /* Observable+Bind.swift in Sources */, D203C50A1BB9C53E00D02D00 /* UILabel+Rx.swift in Sources */, - D2138C901BB9BED600339B5C /* DeinitAction.swift in Sources */, D203C4F51BB9C52900D02D00 /* ItemEvents.swift in Sources */, D2138C911BB9BED600339B5C /* KVOObservable.swift in Sources */, D203C4FA1BB9C53700D02D00 /* RxCollectionViewDataSourceProxy.swift in Sources */, diff --git a/RxCocoa/Common/Observables/Implementations/Deallocating.swift b/RxCocoa/Common/Observables/Implementations/Deallocating.swift deleted file mode 100644 index dcc4849b..00000000 --- a/RxCocoa/Common/Observables/Implementations/Deallocating.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// Deallocating.swift -// RxCocoa -// -// Created by Krunoslav Zaher on 7/12/15. -// Copyright (c) 2015 Krunoslav Zaher. All rights reserved. -// - -import Foundation - -#if !DISABLE_SWIZZLING -class Deallocating : NSObject - , RXDeallocating { - typealias DeallocatingAction = () -> () - - let deallocatingAction: DeallocatingAction - - init(deallocatingAction: DeallocatingAction) { - self.deallocatingAction = deallocatingAction - } - - func deallocating() { - deallocatingAction() - } -} -#endif diff --git a/RxCocoa/Common/Observables/Implementations/DeinitAction.swift b/RxCocoa/Common/Observables/Implementations/DeinitAction.swift deleted file mode 100644 index 112d8d4a..00000000 --- a/RxCocoa/Common/Observables/Implementations/DeinitAction.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// DeinitAction.swift -// RxCocoa -// -// Created by Krunoslav Zaher on 7/12/15. -// Copyright (c) 2015 Krunoslav Zaher. All rights reserved. -// - -import Foundation - -class DeinitAction { - typealias Action = () -> Void - - let action: Action - init(action: Action) { - self.action = action - } - - deinit { - self.action() - } -} \ No newline at end of file diff --git a/RxCocoa/Common/Observables/Implementations/MessageSentObserver.swift b/RxCocoa/Common/Observables/Implementations/MessageSentObserver.swift new file mode 100644 index 00000000..5d4a8036 --- /dev/null +++ b/RxCocoa/Common/Observables/Implementations/MessageSentObserver.swift @@ -0,0 +1,55 @@ +// +// MessageSentObserver.swift +// RxCocoa +// +// Created by Krunoslav Zaher on 7/12/15. +// Copyright (c) 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE + import RxSwift +#endif + +#if !DISABLE_SWIZZLING + + class MessageSentObservable + : ObservableConvertibleType + , RXMessageSentObserver { + typealias E = [AnyObject] + + private let _observable: Observable<[AnyObject]> + private let _observer: AnyObserver<[AnyObject]> + + init(observable: Observable<[AnyObject]>, observer: AnyObserver<[AnyObject]>) { + _observable = observable + _observer = observer + } + + @objc func messageSentWithParameters(parameters: [AnyObject]) -> Void { + _observer.on(.Next(parameters)) + } + + func asObservable() -> Observable<[AnyObject]> { + return _observable + } + + deinit { + _observer.on(.Completed) + } + } + + extension MessageSentObservable { + static func createObserver(replay: Bool) -> MessageSentObservable { + if replay { + let replaySubject = ReplaySubject<[AnyObject]>.create(bufferSize: 1) + return MessageSentObservable(observable: replaySubject.asObservable(), observer: AnyObserver(replaySubject.asObserver())) + } + else { + let publishSubject = PublishSubject<[AnyObject]>() + return MessageSentObservable(observable: publishSubject.asObservable(), observer: AnyObserver(publishSubject.asObserver())) + } + } + } + +#endif diff --git a/RxCocoa/Common/Observables/NSObject+Rx.swift b/RxCocoa/Common/Observables/NSObject+Rx.swift index 8629066a..07d23ac1 100644 --- a/RxCocoa/Common/Observables/NSObject+Rx.swift +++ b/RxCocoa/Common/Observables/NSObject+Rx.swift @@ -139,6 +139,18 @@ extension NSObject { } #endif +class DeallocObservable { + let _subject = ReplaySubject.create(bufferSize: 1) + + init() { + } + + deinit { + _subject.on(.Next(())) + _subject.on(.Completed) + } +} + // Dealloc extension NSObject { @@ -151,24 +163,44 @@ extension NSObject { */ public var rx_deallocated: Observable { return rx_synchronized { - if let subject = objc_getAssociatedObject(self, &deallocatedSubjectContext) as? ReplaySubject { - return subject - } - else { - let subject = ReplaySubject.create(bufferSize: 1) - let deinitAction = DeinitAction { - subject.on(.Next()) - subject.on(.Completed) - } - objc_setAssociatedObject(self, &deallocatedSubjectContext, subject, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - objc_setAssociatedObject(self, &deallocatedSubjectTriggerContext, deinitAction, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - return subject + if let deallocObservable = objc_getAssociatedObject(self, &deallocatedSubjectContext) as? DeallocObservable { + return deallocObservable._subject } + + let deallocObservable = DeallocObservable() + + objc_setAssociatedObject(self, &deallocatedSubjectContext, deallocObservable, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + return deallocObservable._subject } } #if !DISABLE_SWIZZLING - + + public func rx_observeMessage(selector: Selector) -> Observable<[AnyObject]> { + return rx_observeMessage(selector, replay: false) + } + + private func rx_observeMessage(selector: Selector, replay: Bool) -> Observable<[AnyObject]> { + return rx_synchronized { + let rxSelector = RX_selector(selector) + let selectorReference = RX_reference_from_selector(rxSelector) + if let subject = objc_getAssociatedObject(self, selectorReference) as? MessageSentObservable { + return subject.asObservable() + } + + let subject = MessageSentObservable.createObserver(replay) + objc_setAssociatedObject( + self, + selectorReference, + subject, + .OBJC_ASSOCIATION_RETAIN_NONATOMIC + ) + + RX_ensure_swizzled(self.dynamicType, selector) + return subject.asObservable() + } + } + /** Observable sequence of object deallocating events. @@ -178,42 +210,7 @@ extension NSObject { - returns: Observable sequence of object deallocating events. */ public var rx_deallocating: Observable { - return rx_synchronized { - if let subject = objc_getAssociatedObject(self, &deallocatingSubjectContext) as? ReplaySubject { - return subject - } - else { - let subject = ReplaySubject.create(bufferSize: 1) - objc_setAssociatedObject( - self, - &deallocatingSubjectContext, - subject, - .OBJC_ASSOCIATION_RETAIN_NONATOMIC - ) - - let proxy = Deallocating { - subject.on(.Next()) - } - - let deinitAction = DeinitAction { - subject.on(.Completed) - } - - objc_setAssociatedObject(self, - RXDeallocatingAssociatedAction, - proxy, - .OBJC_ASSOCIATION_RETAIN_NONATOMIC - ) - objc_setAssociatedObject(self, - &deallocatingSubjectTriggerContext, - deinitAction, - .OBJC_ASSOCIATION_RETAIN_NONATOMIC - ) - - RX_ensure_deallocating_swizzled(self.dynamicType) - return subject - } - } + return rx_observeMessage("dealloc", replay: true).map { _ in () } } #endif } diff --git a/RxCocoa/Common/_RX.h b/RxCocoa/Common/_RX.h index 357fe913..ac3c131b 100644 --- a/RxCocoa/Common/_RX.h +++ b/RxCocoa/Common/_RX.h @@ -7,10 +7,18 @@ // #import - +#import #if DEBUG # define DLOG(...) NSLog(__VA_ARGS__) #else # define DLOG(...) -#endif \ No newline at end of file +#endif + + +NSArray * __nonnull RX_extract_arguments(NSInvocation * __nonnull invocation); +BOOL RX_is_method_with_description_void(struct objc_method_description method); +BOOL RX_is_method_void(NSInvocation * __nonnull invocation); + +#define SEL_VALUE(x) [NSValue valueWithPointer:(x)] +#define CLASS_VALUE(x) [NSValue valueWithNonretainedObject:(x)] diff --git a/RxCocoa/Common/_RX.m b/RxCocoa/Common/_RX.m index 6ac67328..745a9bdf 100644 --- a/RxCocoa/Common/_RX.m +++ b/RxCocoa/Common/_RX.m @@ -7,3 +7,81 @@ // #import "_RX.h" + +// self + cmd +#define HIDDEN_ARGUMENT_COUNT 2 + +// inspired by https://github.com/ReactiveCocoa/ReactiveCocoa/blob/swift-development/ReactiveCocoa/Objective-C/NSInvocation%2BRACTypeParsing.m +// awesome work +id __nonnull RX_extract_argument_at_index(NSInvocation * __nonnull invocation, NSUInteger index) { + const char *argumentType = [invocation.methodSignature getArgumentTypeAtIndex:index]; + +#define RETURN_VALUE(type) \ + else if (strcmp(argumentType, @encode(type)) == 0) {\ + type val = 0; \ + [invocation getArgument:&val atIndex:index]; \ + return @(val); \ + } + + // Skip const type qualifier. + if (argumentType[0] == 'r') { + argumentType++; + } + + if (strcmp(argumentType, @encode(id)) == 0 + || strcmp(argumentType, @encode(Class)) == 0 + || strcmp(argumentType, @encode(void (^)())) == 0 + ) { + __unsafe_unretained id argument = nil; + [invocation getArgument:&argument atIndex:index]; + return argument; + } + RETURN_VALUE(char) + RETURN_VALUE(int) + RETURN_VALUE(short) + RETURN_VALUE(long) + RETURN_VALUE(long long) + RETURN_VALUE(unsigned char) + RETURN_VALUE(unsigned int) + RETURN_VALUE(unsigned short) + RETURN_VALUE(unsigned long) + RETURN_VALUE(unsigned long long) + RETURN_VALUE(float) + RETURN_VALUE(double) + RETURN_VALUE(BOOL) + RETURN_VALUE(const char *) + else { + NSUInteger size = 0; + NSGetSizeAndAlignment(argumentType, &size, NULL); + NSCParameterAssert(size > 0); + uint8_t data[size]; + [invocation getArgument:&data atIndex:index]; + + return [NSValue valueWithBytes:&data objCType:argumentType]; + } +} + +BOOL RX_is_method_void(NSInvocation * __nonnull invocation) { + const char *methodReturnType = invocation.methodSignature.methodReturnType; + return strcmp(methodReturnType, @encode(void)) == 0; +} + +BOOL RX_is_method_with_description_void(struct objc_method_description method) { + return strncmp(method.types, @encode(void), 1) == 0; +} + +NSArray *RX_extract_arguments(NSInvocation *invocation) { + NSUInteger numberOfArguments = invocation.methodSignature.numberOfArguments; + NSUInteger numberOfVisibleArguments = numberOfArguments - HIDDEN_ARGUMENT_COUNT; + + NSCParameterAssert(numberOfVisibleArguments >= 0); + NSCParameterAssert(RX_is_method_void(invocation)); + + NSMutableArray *arguments = [NSMutableArray arrayWithCapacity:numberOfVisibleArguments]; + + for (NSUInteger index = HIDDEN_ARGUMENT_COUNT; index < numberOfArguments; ++index) { + [arguments addObject:RX_extract_argument_at_index(invocation, index) ?: [NSNull null]]; + } + + return arguments; +} diff --git a/RxCocoa/Common/_RXDelegateProxy.m b/RxCocoa/Common/_RXDelegateProxy.m index a3b14c7e..e33b10d4 100644 --- a/RxCocoa/Common/_RXDelegateProxy.m +++ b/RxCocoa/Common/_RXDelegateProxy.m @@ -7,88 +7,7 @@ // #import "_RXDelegateProxy.h" -#import - -#define SEL_VALUE(x) [NSValue valueWithPointer:(x)] -#define CLASS_VALUE(x) [NSValue valueWithNonretainedObject:(x)] - -// self + cmd -#define HIDDEN_ARGUMENT_COUNT 2 - -// inspired by https://github.com/ReactiveCocoa/ReactiveCocoa/blob/swift-development/ReactiveCocoa/Objective-C/NSInvocation%2BRACTypeParsing.m -// awesome work -id RX_extract_argument_at_index(NSInvocation *invocation, NSUInteger index) { - const char *argumentType = [invocation.methodSignature getArgumentTypeAtIndex:index]; - -#define RETURN_VALUE(type) \ - else if (strcmp(argumentType, @encode(type)) == 0) {\ - type val = 0; \ - [invocation getArgument:&val atIndex:index]; \ - return @(val); \ - } - - // Skip const type qualifier. - if (argumentType[0] == 'r') { - argumentType++; - } - - if (strcmp(argumentType, @encode(id)) == 0 - || strcmp(argumentType, @encode(Class)) == 0 - || strcmp(argumentType, @encode(void (^)())) == 0 - ) { - __unsafe_unretained id argument = nil; - [invocation getArgument:&argument atIndex:index]; - return argument; - } - RETURN_VALUE(char) - RETURN_VALUE(int) - RETURN_VALUE(short) - RETURN_VALUE(long) - RETURN_VALUE(long long) - RETURN_VALUE(unsigned char) - RETURN_VALUE(unsigned int) - RETURN_VALUE(unsigned short) - RETURN_VALUE(unsigned long) - RETURN_VALUE(unsigned long long) - RETURN_VALUE(float) - RETURN_VALUE(double) - RETURN_VALUE(BOOL) - RETURN_VALUE(const char *) - else { - NSUInteger size = 0; - NSGetSizeAndAlignment(argumentType, &size, NULL); - NSCParameterAssert(size > 0); - uint8_t data[size]; - [invocation getArgument:&data atIndex:index]; - - return [NSValue valueWithBytes:&data objCType:argumentType]; - } -} - -BOOL RX_is_method_void(NSInvocation *invocation) { - const char *methodReturnType = invocation.methodSignature.methodReturnType; - return strcmp(methodReturnType, @encode(void)) == 0; -} - -BOOL RX_is_method_with_description_void(struct objc_method_description method) { - return strncmp(method.types, @encode(void), 1) == 0; -} - -NSArray *RX_extract_arguments(NSInvocation *invocation) { - NSUInteger numberOfArguments = invocation.methodSignature.numberOfArguments; - NSUInteger numberOfVisibleArguments = numberOfArguments - HIDDEN_ARGUMENT_COUNT; - - NSCParameterAssert(numberOfVisibleArguments >= 0); - NSCParameterAssert(RX_is_method_void(invocation)); - - NSMutableArray *arguments = [NSMutableArray arrayWithCapacity:numberOfVisibleArguments]; - - for (NSUInteger index = HIDDEN_ARGUMENT_COUNT; index < numberOfArguments; ++index) { - [arguments addObject:RX_extract_argument_at_index(invocation, index) ?: [NSNull null]]; - } - - return arguments; -} +#import "_RX.h" @interface _RXDelegateProxy () diff --git a/RxCocoa/Common/_RXSwizzling.h b/RxCocoa/Common/_RXSwizzling.h index 5a64da95..79d9db2a 100644 --- a/RxCocoa/Common/_RXSwizzling.h +++ b/RxCocoa/Common/_RXSwizzling.h @@ -10,14 +10,15 @@ #if !DISABLE_SWIZZLING -extern void * const RXDeallocatingAssociatedAction; +SEL _Nonnull RX_selector(SEL _Nonnull selector); +void * __nonnull RX_reference_from_selector(SEL __nonnull selector); -@protocol RXDeallocating +@protocol RXMessageSentObserver --(void)deallocating; +-(void)messageSentWithParameters:(NSArray* __nonnull)parameters; @end -void RX_ensure_deallocating_swizzled(Class targetClass); +void RX_ensure_swizzled(Class __nonnull targetClass, SEL __nonnull selector); #endif \ No newline at end of file diff --git a/RxCocoa/Common/_RXSwizzling.m b/RxCocoa/Common/_RXSwizzling.m index 9b91b344..017ac6b1 100644 --- a/RxCocoa/Common/_RXSwizzling.m +++ b/RxCocoa/Common/_RXSwizzling.m @@ -16,18 +16,46 @@ #if !DISABLE_SWIZZLING +SEL _Nonnull RX_selector(SEL __nonnull selector) { + NSString *selectorString = NSStringFromSelector(selector); + return NSSelectorFromString([@"_RX_" stringByAppendingString:selectorString]); +} + +void * __nonnull RX_reference_from_selector(SEL __nonnull selector) { + return selector; +} + +/*static Method RX_methodImplementation(Class __nonnull class, SEL __nonnull selector) { + NSCAssert(class != nil, @"Target class is nil"); + NSCAssert(selector != nil, @"Selector is nil"); + + unsigned int methodCount = 0; + + Method *methods = class_copyMethodList(class, &methodCount); + NSCAssert(methods != nil, @"Methods are nil"); + + @try { + for (unsigned int i = 0; i < methodCount; ++i) { + Method method = methods[i]; + if (method_getName(method) == selector) { + return method; + } + } + } + @finally { + free(methods); + } +}*/ + // inspired by // https://github.com/mikeash/MAZeroingWeakRef/blob/master/Source/MAZeroingWeakRef.m // https://github.com/ReactiveCocoa/ReactiveCocoa/blob/swift-development/ReactiveCocoa/Objective-C/NSObject%2BRACDeallocating.m -int RXDeallocatingAssociatedActionTag = 0; -void * const RXDeallocatingAssociatedAction = &RXDeallocatingAssociatedActionTag; - @interface RXSwizzling: NSObject @property (nonatomic, assign) pthread_mutex_t lock; -@property (nonatomic, strong) NSMutableSet *swizzledDeallocClasses; +@property (nonatomic, strong) NSMutableDictionary*> *swizzledSelectorsByClass; @end @@ -48,8 +76,8 @@ static RXSwizzling *_instance = nil; self = [super init]; if (!self) return nil; - self.swizzledDeallocClasses = [NSMutableSet set]; - + self.swizzledSelectorsByClass = [NSMutableDictionary dictionary]; + pthread_mutexattr_t lock_attr; pthread_mutexattr_init(&lock_attr); pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_RECURSIVE); @@ -65,54 +93,87 @@ static RXSwizzling *_instance = nil; pthread_mutex_unlock(&_lock); } --(void)ensureSwizzledDealloc:(Class)targetClass { - if ([self.swizzledDeallocClasses containsObject:targetClass]) { +-(void)ensureSwizzledSelector:(SEL __nonnull)selector ofClass:(Class __nonnull)targetClass { + NSValue * __nonnull classValue = CLASS_VALUE(targetClass); + NSValue * __nonnull selectorValue = SEL_VALUE(selector); + + NSMutableSet *swizzledSelectorsForClass = self.swizzledSelectorsByClass[classValue]; + + if ([swizzledSelectorsForClass containsObject:selectorValue]) { return; } DLOG(@"Rx is swizzling dealloc for: %@", targetClass); - [self.swizzledDeallocClasses addObject:targetClass]; - NSAssert([self.swizzledDeallocClasses containsObject:targetClass], @"Class should have been swizzled"); - - __block void (*originalDealloc)(__unsafe_unretained id, SEL) = NULL; - - SEL deallocSelector = sel_registerName("dealloc"); - - id swizzledDealloc = ^(__unsafe_unretained id self) { - id action = objc_getAssociatedObject(self, RXDeallocatingAssociatedAction); + + if (swizzledSelectorsForClass == nil) { + swizzledSelectorsForClass = [NSMutableSet set]; + [self.swizzledSelectorsByClass setObject:swizzledSelectorsForClass forKey:classValue]; + } + + [swizzledSelectorsForClass addObject:selectorValue]; + + NSAssert([[self.swizzledSelectorsByClass objectForKey:classValue] containsObject:selectorValue], @"Class should have been swizzled"); + + SEL rxSelector = RX_selector(selector); + + void (^basicImplementation)() = ^(__unsafe_unretained id self) { + id action = objc_getAssociatedObject(self, rxSelector); if (action != nil) { - [action deallocating]; - } - - if (originalDealloc == NULL) { - struct objc_super superInfo = { - .receiver = self, - .super_class = class_getSuperclass(targetClass) - }; - - void (*msgSend)(struct objc_super *, SEL) = (__typeof__(msgSend))objc_msgSendSuper; - msgSend(&superInfo, deallocSelector); - } else { - originalDealloc(self, deallocSelector); + [action messageSentWithParameters:@[]]; } }; - - IMP swizzledDeallocIMP = imp_implementationWithBlock(swizzledDealloc); - - if (!class_addMethod(targetClass, deallocSelector, swizzledDeallocIMP, "v@:")) { - Method deallocMethod = class_getInstanceMethod(targetClass, deallocSelector); - - originalDealloc = (__typeof__(originalDealloc))method_getImplementation(deallocMethod); - originalDealloc = (__typeof__(originalDealloc))method_setImplementation(deallocMethod, swizzledDeallocIMP); + + id newImplementation = ^(__unsafe_unretained id self) { + basicImplementation(self); + struct objc_super superInfo = { + .receiver = self, + .super_class = class_getSuperclass(targetClass) + }; + + void (*msgSend)(struct objc_super *, SEL) = (__typeof__(msgSend))objc_msgSendSuper; + msgSend(&superInfo, selector); + }; + + IMP newImplementationIMP = imp_implementationWithBlock(newImplementation); + + Method existingMethod = class_getInstanceMethod(targetClass, selector); + + NSAssert(existingMethod != nil, @"Method for selector `%@` doesn't exist on `%@`.", NSStringFromSelector(selector), NSStringFromClass(targetClass)); + + const char *encoding = method_getTypeEncoding(existingMethod); + if (class_addMethod(targetClass, selector, newImplementationIMP, encoding)) { + // new dealloc method added, job done + return; } + + // if add fails, that means that method already exists on targetClass + Method existingMethodOnTargetClass = existingMethod; + + // implementation needs to be replaced + __block void (*originalImplementation)(__unsafe_unretained id, SEL) = NULL; + + id implementationReplacement = ^(__unsafe_unretained id self, SEL selector) { + basicImplementation(self, selector); + + originalImplementation(self, selector); + }; + + IMP implementationReplacementIMP = imp_implementationWithBlock(implementationReplacement); + + originalImplementation = (__typeof__(originalImplementation))method_getImplementation(existingMethodOnTargetClass); + NSAssert(originalImplementation != nil, @"Method must exist."); + originalImplementation = (__typeof__(originalImplementation))method_setImplementation(existingMethodOnTargetClass, implementationReplacementIMP); + NSAssert(originalImplementation != nil, @"Method must exist."); } @end -void RX_ensure_deallocating_swizzled(Class targetClass) { +void RX_ensure_swizzled(Class __nonnull targetClass, SEL __nonnull selector) { + NSCAssert(targetClass != nil, @"Target class is nil"); + NSCAssert(selector != nil, @"Selector is nil"); [[RXSwizzling instance] performLocked:^{ - [[RXSwizzling instance] ensureSwizzledDealloc:targetClass]; + [[RXSwizzling instance] ensureSwizzledSelector:selector ofClass:targetClass]; }]; } diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index d992b54e..586d7f3f 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -29,6 +29,7 @@ B18F3BC11BD93E00000AAC79 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */; }; B18F3BE21BDB2E8F000AAC79 /* ReachabilityService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BE11BDB2E8F000AAC79 /* ReachabilityService.swift */; }; B1B7C3D01BE006870076934E /* TakeLast.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B7C3CF1BE006870076934E /* TakeLast.swift */; }; + C80103241C0098FC009B2AE3 /* MessageSentObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80103231C0098FC009B2AE3 /* MessageSentObserver.swift */; }; C803973A1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; C803973B1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; C80397491BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */; }; @@ -267,8 +268,6 @@ C89465701BC6C2BC0055219D /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465221BC6C2BC0055219D /* Logging.swift */; }; C89465711BC6C2BC0055219D /* Observable+Bind.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465231BC6C2BC0055219D /* Observable+Bind.swift */; }; C89465721BC6C2BC0055219D /* ControlTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465261BC6C2BC0055219D /* ControlTarget.swift */; }; - C89465731BC6C2BC0055219D /* Deallocating.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465271BC6C2BC0055219D /* Deallocating.swift */; }; - C89465741BC6C2BC0055219D /* DeinitAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465281BC6C2BC0055219D /* DeinitAction.swift */; }; C89465751BC6C2BC0055219D /* KVOObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465291BC6C2BC0055219D /* KVOObservable.swift */; }; C89465761BC6C2BC0055219D /* KVOObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894652A1BC6C2BC0055219D /* KVOObserver.swift */; }; C89465771BC6C2BC0055219D /* NSNotificationCenter+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894652B1BC6C2BC0055219D /* NSNotificationCenter+Rx.swift */; }; @@ -507,6 +506,7 @@ B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = ""; }; B18F3BE11BDB2E8F000AAC79 /* ReachabilityService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReachabilityService.swift; sourceTree = ""; }; B1B7C3CF1BE006870076934E /* TakeLast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TakeLast.swift; sourceTree = ""; }; + C80103231C0098FC009B2AE3 /* MessageSentObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MessageSentObserver.swift; path = Observables/Implementations/MessageSentObserver.swift; sourceTree = ""; }; C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = ""; }; C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSearchRepositoriesAPI.swift; sourceTree = ""; }; C809E9791BE6841C0058D948 /* Wireframe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Wireframe.swift; sourceTree = ""; }; @@ -689,8 +689,6 @@ C89465221BC6C2BC0055219D /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; C89465231BC6C2BC0055219D /* Observable+Bind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Bind.swift"; sourceTree = ""; }; C89465261BC6C2BC0055219D /* ControlTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlTarget.swift; sourceTree = ""; }; - C89465271BC6C2BC0055219D /* Deallocating.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deallocating.swift; sourceTree = ""; }; - C89465281BC6C2BC0055219D /* DeinitAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeinitAction.swift; sourceTree = ""; }; C89465291BC6C2BC0055219D /* KVOObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVOObservable.swift; sourceTree = ""; }; C894652A1BC6C2BC0055219D /* KVOObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVOObserver.swift; sourceTree = ""; }; C894652B1BC6C2BC0055219D /* NSNotificationCenter+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSNotificationCenter+Rx.swift"; sourceTree = ""; }; @@ -1306,6 +1304,7 @@ C894650D1BC6C2BC0055219D /* Common */ = { isa = PBXGroup; children = ( + C80103231C0098FC009B2AE3 /* MessageSentObserver.swift */, C839740F1BF77406004F02CC /* KVORepresentable.swift */, C83974101BF77406004F02CC /* KVORepresentable+CoreGraphics.swift */, C83974111BF77406004F02CC /* KVORepresentable+Swift.swift */, @@ -1359,8 +1358,6 @@ isa = PBXGroup; children = ( C89465261BC6C2BC0055219D /* ControlTarget.swift */, - C89465271BC6C2BC0055219D /* Deallocating.swift */, - C89465281BC6C2BC0055219D /* DeinitAction.swift */, C89465291BC6C2BC0055219D /* KVOObservable.swift */, C894652A1BC6C2BC0055219D /* KVOObserver.swift */, ); @@ -1734,7 +1731,6 @@ C8297E311B6CF905000589EA /* SearchResultViewModel.swift in Sources */, C8F6A12D1BEF9DA3007DF367 /* CurrentThreadScheduler.swift in Sources */, C89464D91BC6C2B00055219D /* RefCount.swift in Sources */, - C89465731BC6C2BC0055219D /* Deallocating.swift in Sources */, C8F6A1271BEF9DA3007DF367 /* AnonymousInvocable.swift in Sources */, C89464A51BC6C2B00055219D /* Disposable.swift in Sources */, C89464F91BC6C2B00055219D /* ObserverType+Extensions.swift in Sources */, @@ -1774,7 +1770,6 @@ C894659E1BC6C2BC0055219D /* UITextField+Rx.swift in Sources */, C89464D11BC6C2B00055219D /* Merge.swift in Sources */, C89464A71BC6C2B00055219D /* BinaryDisposable.swift in Sources */, - C89465741BC6C2BC0055219D /* DeinitAction.swift in Sources */, C8297E361B6CF905000589EA /* RootViewController.swift in Sources */, C89464BB1BC6C2B00055219D /* AnonymousObservable.swift in Sources */, C89465991BC6C2BC0055219D /* UISegmentedControl+Rx.swift in Sources */, @@ -1850,6 +1845,7 @@ C89465091BC6C2B00055219D /* ReplaySubject.swift in Sources */, C8F6A1281BEF9DA3007DF367 /* InvocableScheduledItem.swift in Sources */, C8297E411B6CF905000589EA /* RxCollectionViewSectionedReloadDataSource.swift in Sources */, + C80103241C0098FC009B2AE3 /* MessageSentObserver.swift in Sources */, C84CC58E1BDD486300E06A64 /* SynchronizedSubscribeType.swift in Sources */, C8F6A12F1BEF9DA3007DF367 /* ImmediateScheduler.swift in Sources */, C89464A81BC6C2B00055219D /* CompositeDisposable.swift in Sources */,