Skip to content

Instantly share code, notes, and snippets.

@vitonzhangtt
Last active April 21, 2021 17:50
Show Gist options
  • Save vitonzhangtt/84335dafb162e91eab5d1a1628cff8ac to your computer and use it in GitHub Desktop.
Save vitonzhangtt/84335dafb162e91eab5d1a1628cff8ac to your computer and use it in GitHub Desktop.
RxSwift Internals Serials: DelegateProxy
/// RxSwift/RxCocoa: 5.1.1
/// DelegateProxyType.swift
public protocol DelegateProxyType: class {
associatedtype ParentObject: AnyObject
associatedtype Delegate
/// ... ...
}
// default implementations
extension DelegateProxyType {
/// Unique identifier for delegate
public static var identifier: UnsafeRawPointer {
/// Generate `delegateIdentifer` from `Delegate` metaType.
let delegateIdentifier = ObjectIdentifier(Delegate.self)
let integerIdentifier = Int(bitPattern: delegateIdentifier)
return UnsafeRawPointer(bitPattern: integerIdentifier)!
}
}
extension DelegateProxyType {
/// Store DelegateProxy subclass to factory.
/// When make 'Rx*DelegateProxy' subclass, call 'Rx*DelegateProxySubclass.register(for:_)' 1 time, or use it in DelegateProxyFactory
/// 'Rx*DelegateProxy' can have one subclass implementation per concrete ParentObject type.
/// Should call it from concrete DelegateProxy type, not generic.
public static func register<Parent>(make: @escaping (Parent) -> Self) {
self.factory.extend(make: make)
}
/// Creates new proxy for target object.
/// Should not call this function directory, use 'DelegateProxy.proxy(for:)'
public static func createProxy(for object: AnyObject) -> Self {
return castOrFatalError(factory.createProxy(for: object))
}
/// Returns existing proxy for object or installs new instance of delegate proxy.
///
/// - parameter object: Target object on which to install delegate proxy.
/// - returns: Installed instance of delegate proxy.
///
///
/// extension Reactive where Base: UISearchBar {
///
/// public var delegate: DelegateProxy<UISearchBar, UISearchBarDelegate> {
/// return RxSearchBarDelegateProxy.proxy(for: base)
/// }
///
/// public var text: ControlProperty<String> {
/// let source: Observable<String> = self.delegate.observe(#selector(UISearchBarDelegate.searchBar(_:textDidChange:)))
/// ...
/// }
/// }
public static func proxy(for object: ParentObject) -> Self {
MainScheduler.ensureRunningOnMainThread()
/// Get the DelegateProxy instance for `object`.
/// In fact, the DelegateProxy instance is an associated object of `object`.
/// see: `assigndProxy(for:)` method.
let maybeProxy = self.assignedProxy(for: object)
let proxy: AnyObject
if let existingProxy = maybeProxy {
proxy = existingProxy
}
else {
/// self.createProxy(for:)
/// --> `factory` property
/// --> DelegateProxyFactory.sharedFactory(for:) // return a `DelegateProxyFactory` instance
/// --> DelegateProxyFactory.createProxy(for:)
/// -->
proxy = castOrFatalError(self.createProxy(for: object))
self.assignProxy(proxy, toObject: object)
assert(self.assignedProxy(for: object) === proxy)
}
let currentDelegate = self._currentDelegate(for: object)
let delegateProxy: Self = castOrFatalError(proxy)
if currentDelegate !== delegateProxy {
delegateProxy._setForwardToDelegate(currentDelegate, retainDelegate: false)
assert(delegateProxy._forwardToDelegate() === currentDelegate)
self._setCurrentDelegate(proxy, to: object)
assert(self._currentDelegate(for: object) === proxy)
assert(delegateProxy._forwardToDelegate() === currentDelegate)
}
/// Return the `delegateProxy` instance.
return delegateProxy
}
}
// private extensions
extension DelegateProxyType {
/// Return a factory instance for each DelegateProxyType type.
private static var factory: DelegateProxyFactory {
/// Find or create a factory for each DelegateProxyType.
/// `self` in Type Method refers to a type (rather than to an instance).
return DelegateProxyFactory.sharedFactory(for: self)
}
private static func assignedProxy(for object: ParentObject) -> AnyObject? {
let maybeDelegate = objc_getAssociatedObject(object, self.identifier)
return castOptionalOrFatalError(maybeDelegate)
}
private static func assignProxy(_ proxy: AnyObject, toObject object: ParentObject) {
objc_setAssociatedObject(object, self.identifier, proxy, .OBJC_ASSOCIATION_RETAIN)
}
}
private class DelegateProxyFactory {
/// The key is the `Delegate metaType`, The value is the DelegateProxyFactory instance.
/// Each Delegate Type has a DelegateProxyFactory instance.
private static var _sharedFactories: [UnsafeRawPointer: DelegateProxyFactory] = [:]
fileprivate static func sharedFactory<DelegateProxy: DelegateProxyType>(for proxyType: DelegateProxy.Type) -> DelegateProxyFactory {
MainScheduler.ensureRunningOnMainThread()
/// Refer to: default implementation of `identifier` property.
/// Generate `identifer` from `Delegate` metaType.
let identifier = DelegateProxy.identifier
if let factory = _sharedFactories[identifier] {
return factory
}
/// Create `factory` for DelegateProxyType type, `proxyType` is the metaType of `DelegateProxy`.
let factory = DelegateProxyFactory(for: proxyType)
/// Record the mapping from `Delegate` metaType to factory instance.
_sharedFactories[identifier] = factory
/// Call DelegateProxy subclass's registerKnownImplementations() method.
/// In subclass's registerKnownImplementations(), calling `register(make:)`,
/// Then register(make:) calls DelegateProxyFactory.extend(make:).
DelegateProxy.registerKnownImplementations()
return factory
}
private var _factories: [ObjectIdentifier: ((AnyObject) -> AnyObject)]
private var _delegateProxyType: Any.Type
private var _identifier: UnsafeRawPointer
private init<DelegateProxy: DelegateProxyType>(for proxyType: DelegateProxy.Type) {
self._factories = [:]
self._delegateProxyType = proxyType /// metaType of DelegateProxyType subclass.
self._identifier = proxyType.identifier /// Ref to: line 13.
}
///
fileprivate func extend<DelegateProxy: DelegateProxyType, ParentObject>(make: @escaping (ParentObject) -> DelegateProxy) {
MainScheduler.ensureRunningOnMainThread()
precondition(self._identifier == DelegateProxy.identifier, "Delegate proxy has inconsistent identifier")
guard self._factories[ObjectIdentifier(ParentObject.self)] == nil else {
rxFatalError("The factory of \(ParentObject.self) is duplicated. DelegateProxy is not allowed of duplicated base object type.")
}
/// The key is the `metaType` of ParentObject.
/// The `factory` create a `DelegateProxy` instance from a ParentObject instance.
/// Install factory for ParentObject, each `ParentObject` type can only have a factory.
///
/// For example:
/// If the `Delegate` is UIScrollViewDelegate, there is only a DelegateProxyFactory instance for
/// this `Delegate` metaType. But the DelegateProxyFactory instance has different factory for different
/// ParentObject type. The `ParentObject` maybe any type in the set: [UIScrollView, UITableView,
/// UICollectionView, UITextView].
///
self._factories[ObjectIdentifier(ParentObject.self)] = { make(castOrFatalError($0)) }
}
fileprivate func createProxy(for object: AnyObject) -> AnyObject {
MainScheduler.ensureRunningOnMainThread()
var maybeMirror: Mirror? = Mirror(reflecting: object)
while let mirror = maybeMirror {
if let factory = self._factories[ObjectIdentifier(mirror.subjectType)] {
/// The type of `object` is `ParentObject`.
return factory(object)
}
maybeMirror = mirror.superclassMirror
}
rxFatalError("DelegateProxy has no factory of \(object). Implement DelegateProxy subclass for \(object) first.")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment