-(IMP __nullable)ensurePrepared:(id __nonnull)target forObserving:(SEL __nonnull)selector error:(NSErrorParam)error {Method instanceMethod =class_getInstanceMethod([target class], selector);if(instanceMethod ==nil){RX_THROW_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain
code:RXObjCRuntimeErrorSelectorNotImplemented
userInfo:nil],nil);}if(selector == @selector(class)|| selector == @selector(forwardingTargetForSelector:)|| selector == @selector(methodSignatureForSelector:)|| selector == @selector(respondsToSelector:)){RX_THROW_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain
code:RXObjCRuntimeErrorObservingPerformanceSensitiveMessages
userInfo:nil],nil);}// For `dealloc` message, original implementation will be swizzled.// This is a special case because observing `dealloc` message is performed when `observeWeakly` is used.//// Some toll free bridged classes don't handle `object_setClass` well and cause crashes.//// To make `deallocating` as robust as possible, original implementation will be replaced.if(selector == deallocSelector){Class __nonnull deallocSwizzingTarget =[target class];IMP interceptorIMPForSelector =[self interceptorImplementationForSelector:selector forClass:deallocSwizzingTarget];if(interceptorIMPForSelector !=nil){return interceptorIMPForSelector;}if(![self swizzleDeallocating:deallocSwizzingTarget error:error]){returnnil;}
interceptorIMPForSelector =[self interceptorImplementationForSelector:selector forClass:deallocSwizzingTarget];if(interceptorIMPForSelector !=nil){return interceptorIMPForSelector;}}}
-(BOOL)ensureSwizzledSelector:(SEL __nonnull)selector
ofClass:(Class __nonnull)classnewImplementationGenerator:(IMP(^)(void))newImplementationGenerator
replacementImplementationGenerator:(IMP(^)(IMP originalImplementation))replacementImplementationGenerator
error:(NSErrorParam)error {if([self interceptorImplementationForSelector:selector forClass:class]!=nil){DLOG(@"Trying to register same intercept at least once, this sounds like a possible bug");returnYES;}
#ifTRACE_RESOURCESatomic_fetch_add(&numberOInterceptedMethods,1);
#endif
DLOG(@"Rx is swizzling `%@` for `%@`",NSStringFromSelector(selector),class);Method existingMethod =class_getInstanceMethod(class, selector);ALWAYS(existingMethod !=nil, @"Method doesn't exist");
const char *encoding =method_getTypeEncoding(existingMethod);ALWAYS(encoding !=nil, @"Encoding is nil");IMP newImplementation =newImplementationGenerator();if(class_addMethod(class, selector, newImplementation, encoding)){// new method added, job done[self registerInterceptedSelector:selector implementation:newImplementation forClass:class];returnYES;}imp_removeBlock(newImplementation);// if add fails, that means that method already exists on targetClassMethod existingMethodOnTargetClass = existingMethod;IMP originalImplementation =method_getImplementation(existingMethodOnTargetClass);ALWAYS(originalImplementation !=nil, @"Method must exist.");IMP implementationReplacementIMP =replacementImplementationGenerator(originalImplementation);ALWAYS(implementationReplacementIMP !=nil, @"Method must exist.");IMP originalImplementationAfterChange =method_setImplementation(existingMethodOnTargetClass, implementationReplacementIMP);ALWAYS(originalImplementation !=nil, @"Method must exist.");// If method replacing failed, who knows what happened, better not trying again, otherwise program can get// corrupted.[self registerInterceptedSelector:selector implementation:implementationReplacementIMP forClass:class];if(originalImplementationAfterChange != originalImplementation){THREADING_HAZARD(class);returnNO;}returnYES;}