Refactors current swizzling code to be more generic.

This commit is contained in:
Krunoslav Zaher 2015-11-21 13:34:54 +01:00
parent c7e73c1184
commit de65c8ff2f
11 changed files with 310 additions and 253 deletions

View File

@ -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 = "<group>"; };
C8093E8D1B8A732E0088E94D /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = "<group>"; };
C8093E901B8A732E0088E94D /* ControlTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlTarget.swift; sourceTree = "<group>"; };
C8093E911B8A732E0088E94D /* Deallocating.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deallocating.swift; sourceTree = "<group>"; };
C8093E921B8A732E0088E94D /* DeinitAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeinitAction.swift; sourceTree = "<group>"; };
C8093E911B8A732E0088E94D /* MessageSentObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageSentObserver.swift; sourceTree = "<group>"; };
C8093E931B8A732E0088E94D /* KVOObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVOObservable.swift; sourceTree = "<group>"; };
C8093E941B8A732E0088E94D /* KVOObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVOObserver.swift; sourceTree = "<group>"; };
C8093E951B8A732E0088E94D /* NSNotificationCenter+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSNotificationCenter+Rx.swift"; sourceTree = "<group>"; };
@ -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 */,

View File

@ -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

View File

@ -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()
}
}

View File

@ -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

View File

@ -139,6 +139,18 @@ extension NSObject {
}
#endif
class DeallocObservable {
let _subject = ReplaySubject<Void>.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<Void> {
return rx_synchronized {
if let subject = objc_getAssociatedObject(self, &deallocatedSubjectContext) as? ReplaySubject<Void> {
return subject
}
else {
let subject = ReplaySubject<Void>.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<Void> {
return rx_synchronized {
if let subject = objc_getAssociatedObject(self, &deallocatingSubjectContext) as? ReplaySubject<Void> {
return subject
}
else {
let subject = ReplaySubject<Void>.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
}

View File

@ -7,10 +7,18 @@
//
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#if DEBUG
# define DLOG(...) NSLog(__VA_ARGS__)
#else
# define DLOG(...)
#endif
#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)]

View File

@ -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;
}

View File

@ -7,88 +7,7 @@
//
#import "_RXDelegateProxy.h"
#import <objc/runtime.h>
#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 ()

View File

@ -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

View File

@ -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<NSValue *, NSMutableSet<NSValue*>*> *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<RXDeallocating> 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<RXMessageSentObserver> 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];
}];
}

View File

@ -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 = "<group>"; };
B18F3BE11BDB2E8F000AAC79 /* ReachabilityService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReachabilityService.swift; sourceTree = "<group>"; };
B1B7C3CF1BE006870076934E /* TakeLast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TakeLast.swift; sourceTree = "<group>"; };
C80103231C0098FC009B2AE3 /* MessageSentObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MessageSentObserver.swift; path = Observables/Implementations/MessageSentObserver.swift; sourceTree = "<group>"; };
C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = "<group>"; };
C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSearchRepositoriesAPI.swift; sourceTree = "<group>"; };
C809E9791BE6841C0058D948 /* Wireframe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Wireframe.swift; sourceTree = "<group>"; };
@ -689,8 +689,6 @@
C89465221BC6C2BC0055219D /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = "<group>"; };
C89465231BC6C2BC0055219D /* Observable+Bind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Bind.swift"; sourceTree = "<group>"; };
C89465261BC6C2BC0055219D /* ControlTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlTarget.swift; sourceTree = "<group>"; };
C89465271BC6C2BC0055219D /* Deallocating.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deallocating.swift; sourceTree = "<group>"; };
C89465281BC6C2BC0055219D /* DeinitAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeinitAction.swift; sourceTree = "<group>"; };
C89465291BC6C2BC0055219D /* KVOObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVOObservable.swift; sourceTree = "<group>"; };
C894652A1BC6C2BC0055219D /* KVOObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVOObserver.swift; sourceTree = "<group>"; };
C894652B1BC6C2BC0055219D /* NSNotificationCenter+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSNotificationCenter+Rx.swift"; sourceTree = "<group>"; };
@ -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 */,