Using method_swizzle + NSProxy to solve the problem of full buried points in App.

Then I learned about NSProxy, the virtual base class, and found that NSProxy could be used to solve the problem of timer cyclic reference, solve the problem of OC multiple inheritance, and make the framework have better expansibility.

Immediately open YYWeakProxy in the project to have a look, and find that it is achieved by message forwarding. In my walnut-sized head, the message forwarding is still stuck in the interception of resolving the crash when the in-app method is not implemented.

An immediate search for the purpose of message forwarding turned up two interesting frameworks:

  • CTMediator solves the componentization problem
  • Aspects are used for iOS hooks

Ps: All are old frame, do not hinder learning.

This paper is the study of Aspects framework.

1. Sample Demo:

The following example comes from The Aspects Demo:

// ViewController.m

@interface** ViewController (a)

@end

@implementation** ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    UIViewController *testController = [[UIImagePickerController alloc] init];
    testController.modalPresentationStyle = UIModalPresentationFormSheet;
    [self presentViewController:testController animated:YES completion:NULL];
    
    [testController aspect_hookSelector: @selector(viewWillDisappear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info, BOOL animated) {
        NSLog(@"Popped Hello from Aspects", [info instance]);
    } error:NULL];

}
Copy the code

2. Method list

/** Aspects uses Objective-C message forwarding to hook into messages. This will create some overhead. Don't add aspects  to methods that are called a lot. Aspects is meant for view/controller code that is not called a 1000 times per second.  Adding aspects returns an opaque token which can be used to deregister again. All calls are thread safe. */
@interface NSObject (Aspects)

/// Adds a block of code before/instead/after the current `selector` for a specific class.
///
/// @param block Aspects replicates the type signature of the method being hooked.
/// The first parameter will be `id<AspectInfo>`, followed by all parameters of the method.
/// These parameters are optional and will be filled to match the block signature.
/// You can even use an empty block, or one that simple gets `id<AspectInfo>`.
///
/// @note Hooking static methods is not supported.
/// @return A token which allows to later deregister the aspect.
+ (id<AspectToken>)aspect_hookSelector:(SEL)selector
                      withOptions:(AspectOptions)options
                       usingBlock:(id)block
                            error:(NSError **)error;

/// Adds a block of code before/instead/after the current `selector` for a specific instance.
- (id<AspectToken>)aspect_hookSelector:(SEL)selector
                      withOptions:(AspectOptions)options
                       usingBlock:(id)block
                            error:(NSError **)error;

@end
Copy the code

Aspects is a class of NSObject, with only two methods, a class method and an instance method, and the invocation of instance methods is investigated using the code in the Demo.

3. Call the process

Describes the process of calling the master. The process of destroying is not included in this paper.

4. Code analysis

Following the code analysis, most of the understanding is commented out in the code.

1, the entry method aspect_add

static id aspect_add(id self, SEL selector, AspectOptions options, id block, NSError **error) {
    NSCParameterAssert(self);
    NSCParameterAssert(selector);
    NSCParameterAssert(block);

    __block AspectIdentifier *identifier = nil;
    
    Os_unfair_lock = os_unfair_lock
    aspect_performLocked(^{
        if (aspect_isSelectorAllowedAndTrack(self, selector, options, error)) {
            // Load or create the aspect container.
            AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector);
            // Create an AspectIdentifier object
            identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error];
            if (identifier) {
                // Stores AspectIdentifier objects classified by options
                [aspectContainer addAspect:identifier withOptions:options];

                // Modify the class to allow message interception.
                // Modify the class to allow message interception.aspect_prepareClassAndHookSelector(self, selector, error); }}});return identifier;
}
Copy the code

2, whether the method can hook aspect_isSelectorAllowedAndTrack

Current method of filtering the retain, release, autorelease, forwardInvocation: can’t hook method, and the hook before dealloc method can only be destroyed.

static BOOL aspect_isSelectorAllowedAndTrack(NSObject *self, SEL selector, AspectOptions options, NSError **error) {
    static NSSet *disallowedSelectorList;
    static dispatch_once_t pred;
    dispatch_once(&pred, ^{
        /// These methods do not allow hooks
        disallowedSelectorList = [NSSet setWithObjects:@"retain"The @"release"The @"autorelease"The @"forwardInvocation:", nil];
    });

    // Check against the blacklist.
    
    // disallowedSelectorList returns NO and does not allow hooks
    NSString *selectorName = NSStringFromSelector(selector);
    if ([disallowedSelectorList containsObject:selectorName]) {
        NSString *errorDescription = [NSString stringWithFormat:@"Selector %@ is blacklisted.", selectorName];
        AspectError(AspectErrorSelectorBlacklisted, errorDescription);
        return NO;
    }

    // Additional checks.
    
    // Check that the dealloc method can only hook before destruction.
    AspectOptions position = options&AspectPositionFilter;
    if ([selectorName isEqualToString:@"dealloc"] && position ! = AspectPositionBefore) { NSString *errorDesc = @"AspectPositionBefore is the only valid position when hooking dealloc.";
        AspectError(AspectErrorSelectorDeallocPosition, errorDesc);
        return NO;
    }

    // Check the method when implemented, not implemented hook, no meaning.
    if(! [self respondsToSelector:selector] && ! [self.class instancesRespondToSelector:selector]) { NSString *errorDesc = [NSString stringWithFormat:@"Unable to find selector -[%@ %@].", NSStringFromClass(self.class), selectorName];
        AspectError(AspectErrorDoesNotRespondToSelector, errorDesc);
        return NO;
    }
    
    // Search for the current class and the class hierarchy IF we are modifying a class object
    // If we are modifying a class object, search the current class and its hierarchy
    if (class_isMetaClass(object_getClass(self))) {
        /// enter the hook class method
        Class klass = [self class];
        NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict();
        Class currentClass = [self class];
        do {
            /// prevent repeated hooks
            AspectTracker *tracker = swizzledClassesDict[currentClass];
            if ([tracker.selectorNames containsObject:selectorName]) {

                // Find the topmost class for the log.
                if (tracker.parentEntry) {
                    AspectTracker *topmostEntry = tracker.parentEntry;
                    while (topmostEntry.parentEntry) {
                        topmostEntry = topmostEntry.parentEntry;
                    }
                    NSString *errorDescription = [NSString stringWithFormat:@"Error: %@ already hooked in %@. A method can only be hooked once per class hierarchy.", selectorName, NSStringFromClass(topmostEntry.trackedClass)];
                    AspectError(AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription);
                    return NO;
                }else if (klass == currentClass) {
                    // Already modified and topmost!
                    returnYES; }}}while ((currentClass = class_getSuperclass(currentClass)));

        // Add the selector as being modified.
        currentClass = klass;
        AspectTracker *parentTracker = nil;
        do {
            // add the dictionary to the parent
            AspectTracker *tracker = swizzledClassesDict[currentClass];
            if(! tracker) { tracker = [[AspectTracker alloc] initWithTrackedClass:currentClass parent:parentTracker]; swizzledClassesDict[(id<NSCopying>)currentClass] = tracker; } [tracker.selectorNames addObject:selectorName];// All superclasses get marked as having a subclass that is modified.
            parentTracker = tracker;
        }while ((currentClass = class_getSuperclass(currentClass)));
    }

    return YES;
}
Copy the code

3. Get aspect_getContainerForObject

// Loads or creates the aspect container.
// Load or create the aspect container.
static AspectsContainer *aspect_getContainerForObject(NSObject *self, SEL selector) {
    NSCParameterAssert(self);
    
    // Prefix methods with aspects_ xx
    SEL aliasSelector = aspect_aliasForSelector(selector);
    
    // initialize the AspectsContainer object, associate it with the current object,
    AspectsContainer *aspectContainer = objc_getAssociatedObject(self, aliasSelector);
    if(! aspectContainer) { aspectContainer = [AspectsContainernew];
        objc_setAssociatedObject(self, aliasSelector, aspectContainer, OBJC_ASSOCIATION_RETAIN);
    }
    return aspectContainer;
}
Copy the code

4. Initialize AspectIdentifier

In this method, just focus on aspect_blockMethodSignature to get the block’s method signature.

+ (instancetype)identifierWithSelector:(SEL)selector object:(id)object options:(AspectOptions)options block:(id)block error:(NSError **)error {
    NSCParameterAssert(block);
    NSCParameterAssert(selector);
    
    // get the block's method signature
    NSMethodSignature *blockSignature = aspect_blockMethodSignature(block, error); // TODO: check signature compatibility, etc.
    
    /// verify that the parameters in the block are compatible with the parameters in the hook method
    if(! aspect_isCompatibleBlockSignature(blockSignature, object, selector, error)) {return nil;
    }

    /// Build identifier objects
    AspectIdentifier *identifier = nil;
    if (blockSignature) {
        identifier = [AspectIdentifier new];
        identifier.selector = selector;
        identifier.block = block;
        identifier.blockSignature = blockSignature;
        identifier.options = options;
        identifier.object = object; // weak
    }
    return identifier;
}
Copy the code
4.1 Obtaining the block’s method signature aspect_blockMethodSignature

In this method, focus on the first line AspectBlockRef layout = (__bridge void *)block; , Bridges the block to facilitate the value.

The bridge structure is as follows:

// Block internals.
typedef NS_OPTIONS(int, AspectBlockFlags) {
	AspectBlockFlagsHasCopyDisposeHelpers = (1 << 25),
	AspectBlockFlagsHasSignature          = (1 << 30)};typedef struct _AspectBlock {
	__unused Class isa;
	AspectBlockFlags flags;
	__unused int reserved;
	void(__unused *invoke)(struct _AspectBlock *block, ...) ;struct {
		unsigned long int reserved;
		unsigned long int size;
		// requires AspectBlockFlagsHasCopyDisposeHelpers
		void (*copy)(void *dst, const void *src);
		void (*dispose)(const void *);
		// requires AspectBlockFlagsHasSignature
		const char *signature;
		const char *layout;
	} *descriptor;
	// imported variables
} *AspectBlockRef;
Copy the code

Source code implementation:

// get the block's method signature
static NSMethodSignature *aspect_blockMethodSignature(id block, NSError **error) {
    // It is possible to bridge blocks because of their internal structure.
    / / Block that you are like (a) https://juejin.cn/post/6955409380321787940
    / / Block, the original you are such a (2) https://juejin.cn/post/6956895256998576164

    AspectBlockRef layout = (__bridge void *)block;
    
    // Generate rules internally according to flags. Check BlockFlagsHasSignature returns nil if no block method is signed
	if(! (layout->flags & AspectBlockFlagsHasSignature)) { NSString *description = [NSString stringWithFormat:@"The block %@ doesn't contain a type signature.", block];
        AspectError(AspectErrorMissingBlockSignature, description);
        return nil;
    }
    
    /// Get the first address of the descriptor
	void *desc = layout->descriptor;
    
    /** struct { unsigned long int reserved; unsigned long int size; // requires AspectBlockFlagsHasCopyDisposeHelpers void (*copy)(void *dst, const void *src); void (*dispose)(const void *); // requires AspectBlockFlagsHasSignature const char *signature; const char *layout; } *descriptor; * /
    
    // Offset by 2 int units (pointer over unsigned long int reserved; And unsigned long int size;)
	desc += 2 * sizeof(unsigned long int);
    
    / / void * if BlockFlagsHasCopyDisposeHelpers deviation of 2 units (pointer through the void (* copy) and void * the dispose ())
	if (layout->flags & AspectBlockFlagsHasCopyDisposeHelpers) {
		desc += 2 * sizeof(void *);
    }
    
    // Current pointer to const char *signature;
    // If the pointer address does not exist. If signature does not exist, return nil
	if(! desc) { NSString *description = [NSString stringWithFormat:@"The block %@ doesn't has a type signature.", block];
        AspectError(AspectErrorMissingBlockSignature, description);
        return nil;
    }
     
    // Return NSMethodSignature object
	const char *signature = (*(const char **)desc);
	return [NSMethodSignature signatureWithObjCTypes:signature];
}
Copy the code

If you don’t understand how the bridge works, take a look at these two articles, which examine the internal structure of the Block.

  • So this is what you look like (1)
  • Block so you look like this (two)

5, the parameters in the test block, and the parameters of the hook method is compatible aspect_isCompatibleBlockSignature

/// verify that the parameters in the block are compatible with the parameters in the hook method
static BOOL aspect_isCompatibleBlockSignature(NSMethodSignature *blockSignature, id object, SEL selector, NSError **error) {
    NSCParameterAssert(blockSignature);
    NSCParameterAssert(object);
    NSCParameterAssert(selector);

    // The signature matches
    BOOL signaturesMatch = YES;

    // get the method signature that requires hook SEL
    NSMethodSignature *methodSignature = [[object class] instanceMethodSignatureForSelector:selector];
    
    /** * example: hook the viewWillDisappear * ArgumentTypeAtIndex * 1: self * 2: SEL * 3: bool (animated) * * blockSignature: ArgumentTypeAtIndex * 1: self * 2: AspectInfo * 3: Bool (animated) * If the hook method itself has only 2 arguments such as: Dealloc (self,SEL) * BlockSignature three Argument (self, SEL, bool) * if such blockSignature numberOfArguments > methodSignature. NumberOfArguments It doesn't match. * So this method allows blockSignature to be less than the arguments for methodSignature (some not), but no more. * /
    if (blockSignature.numberOfArguments > methodSignature.numberOfArguments) {
        signaturesMatch = NO;
    }else {
        if (blockSignature.numberOfArguments > 1) {
            /// We need to force the second argument to be AspectInfo
            const char *blockType = [blockSignature getArgumentTypeAtIndex:1];
            if (blockType[0] != The '@') { signaturesMatch = NO; }}// Argument 0 is self/block, argument 1 is SEL or id<AspectInfo>. We start comparing at argument 2.
        // The block can have less arguments than the method, that's ok.
        
        // Argument 0 is self/block, and argument 1 is SEL or ID. Let's start with index = 2.
        // Blocks can have fewer arguments than methods, which is fine.
        if (signaturesMatch) {
            for (NSUInteger idx = 2; idx < blockSignature.numberOfArguments; idx++) {
                const char *methodType = [methodSignature getArgumentTypeAtIndex:idx];
                const char *blockType = [blockSignature getArgumentTypeAtIndex:idx];
                // Only compare parameter, not the optional type data.
                
                // This allows blockSignature to be small, but does not allow skipping arguments.
                // For example, the hook method has 4 arguments (self,SEL,Bool,Int)
                // Block can (self,SEL,Bool) not (self,SEL,Int)
                if(! methodType || ! blockType || methodType[0] != blockType[0]) {
                    signaturesMatch = NO;
                    break; }}}}/// Return No if there is No print error
    if(! signaturesMatch) { NSString *description = [NSString stringWithFormat:@"Blog signature %@ doesn't match %@.", blockSignature, methodSignature];
        AspectError(AspectErrorIncompatibleBlockSignature, description);
        return NO;
    }
    return YES;
}
Copy the code

See here NSMethodSignature * methodSignature = [[object class] instanceMethodSignatureForSelector: selector]; To acquiring hook SEL method signature found that when using the instanceMethodSignatureForSelector, so in need of hook type method, requires the incoming class. The metaClass.

6, changing the class to allow message interception aspect_prepareClassAndHookSelector

This method is the core method, implemented using ISA_swizzle.

Soul painting, never mind.

A brief introduction to the process:

There is a class MyClass that derives an object myObj and now needs the hook helloMethod method.

  • Aspect framework throughaspect_hookClassMethod to generate aMyClassA subclass ofMyClass_Aspect_
  • willmyObjisaPoint to theMyClass_Aspect_
  • To obtainMyClass helloMethodmethodsIMP
  • toMyClass_Aspect_Add method toaspect_helloMethodMethod, and willIMPPoint to thehelloMethodmethodsIMP(Actually copyhelloMethodMethod implementation)
  • Replace or addMyClass_Aspect_Of a list of methods in a classforwardInvcationTo let itIMPPoints to the Aspect framework’s own implementation__ASPECTS_ARE_BEING_CALLED__Self handling method forwarding
  • Replace or addMyClass_Aspect_Of a list of methods in a classhelloMethodMethod, let itIMPPoint to the_objc_msgForward

When you call MyClass to derive the instance method helloMethod of myObj, you’re actually calling the helloMethod method in MyClass_Aspect_.

But helloMethod’s IMP is _objc_msgForward and calls the Aspect framework’s own implementation of IMP __ASPECTS_ARE_BEING_CALLED__.

/// prepare the class and hook selector
static void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector, NSError **error) {
    NSCParameterAssert(selector);
    Class klass = aspect_hookClass(self, error);
    
    /// klass is a subclass of the original class. So you can get the method of the parent class
    Method targetMethod = class_getInstanceMethod(klass, selector);
    
    /// get method implementation
    IMP targetMethodIMP = method_getImplementation(targetMethod);
    
   
    
    if(! aspect_isMsgForwardIMP(targetMethodIMP)) {// Make a method alias for the existing method implementation, it not already copied.
        const char *typeEncoding = method_getTypeEncoding(targetMethod);
        
        /// Add the prefix AspectsMessagePrefix_ to the method
        SEL aliasSelector = aspect_aliasForSelector(selector);
        
        /// If the new class klass does not implement aliasSelector
        if(! [klass instancesRespondToSelector:aliasSelector]) {/// klass class added AspectsMessagePrefix_xxx copy XXX implementation
            __unused BOOL addedAlias = class_addMethod(klass, aliasSelector, method_getImplementation(targetMethod), typeEncoding);
            NSCAssert(addedAlias, @"Original implementation for %@ is already copied to %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), klass);
        }
        
        /** * Klass (Class_Aspects_) * aspects__viewWillDisappear (add in aspect_prepareClassAndHookSelector. Class_addMethod) * class (in Added at aspect_hookedge class) * forwardInvocation (added at aspect_hookClass -> aspect_swizzleForwardInvocation) */


        // We use forwardInvocation to hook in.
        // Add the original selector method to the subclass and make the IMP point to _objc_msgForward.
        class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);
        AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
        
        * viewWillDisappear: IMP -> _objc_msgForward * aspects__viewWillDisappear: Imp -> viewWillDisappear * class IMP -> Original CLS * forwardInvocation: IMP -> __ASPECTS_ARE_BEING_CALLED__ */}}Copy the code
6.1 Dynamically generate a subclass aspect_hookClass
static Class aspect_hookClass(NSObject *self, NSError **error) {
    NSCParameterAssert(self);
    /// object_getClass is the class of the object if self is an object, or the metaclass if self isa class
    /// class self takes the class for the object, if self is the class, returns itself.
    // if self is the object, then statedClass == baseClass
    Class statedClass = self.class;
    Class baseClass = object_getClass(self);
    NSString *className = NSStringFromClass(baseClass);

    // Already subclassed
    if ([className hasSuffix:AspectsSubclassSuffix]) {
            return baseClass;

        // We swizzle a class object, not a single object.
        // We are mixing a class object instead of a single object.
    } else if (class_isMetaClass(baseClass)) {
        return aspect_swizzleClassInPlace((Class)self);
        // Probably a KVO'ed class. Swizzle in place. Also swizzle meta classes in place.
    } else if(statedClass ! = baseClass) {return aspect_swizzleClassInPlace(baseClass);
    }

    // Default case. Create dynamic subclass.
    const char *subclassName = [className stringByAppendingString:AspectsSubclassSuffix].UTF8String;
    Class subclass = objc_getClass(subclassName);

    if (subclass == nil) {
         // Create a class dynamically
         subclass = objc_allocateClassPair(baseClass, subclassName, 0);
         if (subclass == nil) {
        // Create failed, return null
        NSString *errrorDesc = [NSString stringWithFormat:@"objc_allocateClassPair failed to allocate class %s.", subclassName];
       AspectError(AspectErrorFailedToAllocateClassPair, errrorDesc);
          return nil;
      }
        // add the forwardInvocation method
        aspect_swizzleForwardInvocation(subclass);
        
        /** * object_getClass = 'isa'; /** * The difference between object_getClass(obj) and [obj class] is two: * 1, if object_getClass is an instance object, they are the same; * 2, if it isa class object, class is self and object_getClass is isa */
        
        // class derived object gets class object,
	aspect_hookedGetClass(subclass, statedClass);
        
        // Prevent a class object derived from a metaclass from getting a metaclass
	aspect_hookedGetClass(object_getClass(subclass), statedClass);
        
        /// dynamically register classes
	objc_registerClassPair(subclass);
    }

    // change the isa pointer to our subclass
    object_setClass(self, subclass);
    return subclass;
}
Copy the code
6.2 adding aspect_swizzleForwardInvocation
static NSString *const AspectsForwardInvocationSelectorName = @"__aspects_forwardInvocation:";
static void aspect_swizzleForwardInvocation(Class klass) {
    NSCParameterAssert(klass);
    // If there is no method, replace will act like class_addMethod.
    // If there is no method, replace plays a role similar to class_addMethod.
    
    // class_replaceMethod: if there is a method to replace, replace the IMP of the original method and return the IMP of the original method; if there is no original method, add the method dynamically and return nil
    
  
    // if the forwardInvocation is implemented, the IMP will be replaced with __ASPECTS_ARE_BEING_CALLED__
    // If not, add a forwardInvocation IMP to __ASPECTS_ARE_BEING_CALLED__
    IMP originalImplementation = class_replaceMethod(klass, @selector(forwardInvocation:), (IMP)__ASPECTS_ARE_BEING_CALLED__, "v@:@");
    
    // determine if forwardInvocation is implemented
    if (originalImplementation) {
        // Add an __aspects_forwardInvocation method,
        class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName), originalImplementation, "v@:@");
    }
    AspectLog(@"Aspects: %@ is now aspect aware.", NSStringFromClass(klass));
}
Copy the code
6.3 Change the aspect_hookedGetClass returned by @selector(class)

This method is used in the iOS framework KVO, and a subclass is derived for keyvalue listening, but calling the class method and objc_getClass() method return different results.

static void aspect_hookedGetClass(Class class, Class statedClass) {
    NSCParameterAssert(class);
    NSCParameterAssert(statedClass);
	Method method = class_getInstanceMethod(class, @selector(class));
	IMP newIMP = imp_implementationWithBlock(^(id self) {
		return statedClass;
	});
    
    /// Add the class method and return the previous class name
	class_replaceMethod(class, @selector(class), newIMP, method_getTypeEncoding(method));
}
Copy the code

7. Aspects’ own message forwardingASPECTS_ARE_BEING_CALLED

This method is to get the object message forward, handle the Before hooks call block, and then handle the Instead hooks if there are method replacements. If there is no method replacement, loop back to the parent class for the calling method, and finally handle After hooks, which are destroyed.

// This is the swizzled forwardInvocation: method.
static void __ASPECTS_ARE_BEING_CALLED__(__unsafe_unretained NSObject *self, SEL selector, NSInvocation *invocation) {
    NSCParameterAssert(self);
    NSCParameterAssert(invocation);
    SEL originalSelector = invocation.selector;
	SEL aliasSelector = aspect_aliasForSelector(invocation.selector);
    invocation.selector = aliasSelector;
    
    /// handle the object's hook
    AspectsContainer *objectContainer = objc_getAssociatedObject(self, aliasSelector);
    
    /// handle the hooks of the class
    AspectsContainer *classContainer = aspect_getContainerForClass(object_getClass(self), aliasSelector);
    
    AspectInfo *info = [[AspectInfo alloc] initWithInstance:self invocation:invocation];
    NSArray *aspectsToRemove = nil;

    // Before hooks.
    aspect_invoke(classContainer.beforeAspects, info);
    aspect_invoke(objectContainer.beforeAspects, info);

    // Instead hooks.
    BOOL respondsToAlias = YES;
    if (objectContainer.insteadAspects.count || classContainer.insteadAspects.count) {
        aspect_invoke(classContainer.insteadAspects, info);
        aspect_invoke(objectContainer.insteadAspects, info);
    }else {
        // call the original method if there is no replacement method
        Class klass = object_getClass(invocation.target);
        do {
            if ((respondsToAlias = [klass instancesRespondToSelector:aliasSelector])) {
                [invocation invoke];
                break; }}while(! respondsToAlias && (klass = class_getSuperclass(klass))); }// After hooks.
    aspect_invoke(classContainer.afterAspects, info);
    aspect_invoke(objectContainer.afterAspects, info);

    // If no hooks are installed, call original implementation (usually to throw an exception)
    if(! respondsToAlias) { invocation.selector = originalSelector; SEL originalForwardInvocationSEL = NSSelectorFromString(AspectsForwardInvocationSelectorName);if ([self respondsToSelector:originalForwardInvocationSEL]) {
            ((void( *)(id, SEL, NSInvocation *))objc_msgSend)(self, originalForwardInvocationSEL, invocation);
        }else{ [self doesNotRecognizeSelector:invocation.selector]; }}// Remove any hooks that are queued for deregistration.
    [aspectsToRemove makeObjectsPerformSelector:@selector(remove)];
}
Copy the code

5, conclusion

This is the end of the Aspects analysis. This paper only analyzes the hook process of Aspects, and does not list the destruction process analysis of Aspects. If you are interested, you can download the source code.

And finally, happy New Year’s Day 2022.