IOS how to achieve Aspect Oriented Programming (on)

IOS how to achieve Aspect Oriented Programming (on)

Preface

In the Runtime hospital after two days of hospitalization, analysis of the realization of the principle of AOP. After discharge, found that the Aspect library has not been analyzed in detail, so there is this article, today to talk about how to achieve Aspect iOS Oriented Programming.

Catalog

  • 1.Aspect Oriented Programming profile
  • 2 what is Aspects
  • Analysis of 4 basic classes in 3.Aspects
  • Preparation before 4.Aspects hook
  • 5.Aspects hook process
  • 6 some of the pits on Aspects”

A.Aspect Oriented Programming profile

Aspect oriented programming (aspect-oriented programming, AOP, and the translation oriented program design, aspect oriented programming, aspect oriented programming) is a term in computer science, refers to a programming paradigm. This paradigm in a called side (aspect, also translated as aspects of language) based on the structure, the side is a new modular mechanism, used to describe the dispersion in the object, class or function of crosscutting concerns (crosscutting concern).

The concept of the side of the object from the object-oriented programming improvements, but not limited to this, it can also be used to improve the traditional function. Side – to – side programming concepts also include meta object protocols, themes (subject), mixin.

A technology of AOP to realize the unified maintenance of program functions through pre compiler and runtime dynamic proxy.

OOP (object oriented programming) for the business process of the entities and their attributes and behavior of abstract encapsulation, in order to obtain a clearer and more efficient division of logical units.

AOP is to extract the cut section in the process of business processing, it is faced with a certain step or phase in the process of processing, in order to obtain the logic process of the low coupling between the isolation effect.

OOP and AOP belong to two different ways of thinking”. OOP focuses on the attributes of the object and the behavior of the package, AOP focused on the processing of a certain step and stage, from which the extraction of cut.

For example, if there is a need to determine the permissions, OOP approach must be added before each operation to determine the permissions. What about the log? Log records are added at the end of each method. AOP is to repeat the logic and operation of these extracted, the use of dynamic agents to achieve the decoupling of these modules. OOP and AOP are not mutually exclusive, but with each other.

In the iOS using AOP programming, can achieve non intrusive. You can add new functionality without changing the previous code logic. It is mainly used to deal with some of the cross cutting nature of the system services, such as logging, rights management, caching, object pool management.

Two. What is Aspects

IOS how to achieve Aspect Oriented Programming (on)

Aspects is a lightweight aspect oriented programming library. It allows you to add any code in each class and in each instance. You can insert code in the following pointcut: before (before the original method is executed) / instead (replace the original method execution) / after (after the original method is executed, default). Runtime message forwarding through Hook. Aspects will automatically call the super method, the use of method swizzling will be more convenient.

This library is very stable, currently used in hundreds of APP on the. It is also part of PSPDFKit, PSPDFKit is a iOS look at the PDF framework library. The author finally decided to open it up.

Analysis of 4 basic classes in three.Aspects

We start with the paper.

1.Aspects.h
Typedef NS_OPTIONS (NSUInteger, AspectOptions) {AspectPositionAfter = 0, after the original implementation / / / Called (default) AspectPositionInstead = 1, Will replace the original implementation. / / / / / / AspectPositionBefore = 2, Called before the original implementation. AspectOptionAutomaticRemoval < = 1; < 3; Will remove the hook after / / / the first execution.};

An enumeration is defined in the header file. This enumeration is called the time slicing method. By default, AspectPositionAfter calls after the original method is executed. AspectPositionInstead is the replacement of the original method. AspectPositionBefore is called the slicing method before the original method. AspectOptionAutomaticRemoval is automatically removed after hook execution.

@protocol AspectToken < NSObject> - (BOOL) remove; @end

Defines a AspectToken protocol, where the Aspect Token is implicit, allowing us to call remove to revoke a hook. The remove method returns the YES on behalf of the revocation success, and returns NO to undo the failure.

@protocol AspectInfo < NSObject> - (ID) instance; - (NSInvocation *) originalInvocation; - (NSArray *) arguments;

An AspectInfo protocol is also defined. AspectInfo protocol is the first parameter in our block syntax.

The instance method returns an instance of the current hook. The originalInvocation method returns the original invocation by the hooked method. The arguments method returns the parameters of all methods. Its implementation is lazy loading.

The first document also deliberately gave a note to explain the usage and attention of Aspects, it is worth our attention.

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 per second. Adding aspects 1000 times returns an opaque token which can be used to deregister again. All calls are thread safe..

Aspects uses the OC message forwarding mechanism, hook message. There will be some performance overhead. Don’t add Aspects to a method that is often used. Aspects is designed to be used for view/controller code, instead of using hook to call 1000 times per second.

After adding Aspects, an implicit token is returned, and the token is used to write off the hook method. All calls are thread safe.

Thread safety, the following will be analyzed in detail. Now at least we know that Aspects should not be used in the for cycle of these methods, which will cause great loss of performance.

@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 using Block: (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

There are only two of these methods in the Aspects library. Here you can see, Aspects is a NSobject extension, as long as the NSObject, you can use these two methods. The name of this two methods are the same, the parameter and return value is the same, the only difference is that a method is a plus minus method. One is used to hook class methods, one is used to hook instance method.

There are 4 entries in the method. The first selector is the original method to increase the tangent. The second parameter is the AspectOptions type that is representative of this slice by adding before / instead / after in the original method. The fourth parameter is the returned error.

The focus is on the third block. This block copies the signature signature type being used by the hook method. Block follows the AspectInfo protocol. We can even use an empty block. AspectInfo protocol parameters are optional, mainly used to match the block signature.

The return value is a token that can be used to write off the Aspects.

Note that Aspects is not supported by the hook static static method

Typedef NS_ENUM (NSUInteger, AspectErrorCode) {AspectErrorSelectorBlacklisted, like retain, Selectors / / / release, autorelease are blacklisted. AspectErrorDoesNotRespondToSelector Selector could not be found. AspectErrorSelectorDeallocPosition / / / / / / When, hooking dealloc, only AspectPositionBefore is allowed. AspectErrorSelectorAlreadyHookedInClassHierarchy Statically hooking the same method / / / in subclasses is not allowed. AspectErrorFailedToAllocateClassPair The runtime failed creating a / / / class pair. AspectErrorMissingBlockSignature. The block misses compile time / / / signature info and can't be called. AspectErrorIncompatibleBloc KSignature, The block signature does not / / / match the method or is too large. AspectErrorRemoveObjectAlreadyDeallocated = 100 (for removing) The / / / object hooked is already deallocated. extern NSString *const AspectErrorDomain};

The type of error code is defined here. Error when we debug.

2.Aspects.m
#import "Aspects.h" #import < libkern/OSAtomic.h> #import < objc/runtime.h> #import < objc/message.h>

#import < libkern/OSAtomic.h> import this header file for the following spin lock. #import < objc/runtime.h> and #import < objc/message.h> is necessary to use Runtime header file.

Typedef NS_OPTIONS (int, AspectBlockFlags) {AspectBlockFlagsHasCopyDisposeHelpers = (1 < < 25), AspectBlockFlagsHasSignature = (< <);};

Defines the AspectBlockFlags, which is a flag that is used to mark two cases, whether Copy and Dispose are required for Helpers, and whether the method signature is required Signature.

The 4 classes defined in Aspects are AspectInfo, AspectIdentifier, AspectsContainer, AspectTracker. Then look at how these 4 categories are defined.

3 AspectInfo
@interface AspectInfo: NSObject < AspectInfo> - (ID) initWithInstance: (__unsafe_unretained ID) instance invocation: (NSInvocation *) invocation; @property (nonatomic, unsafe_unretained, readonly) id instance; @property (nonatomic, strong, readonly) NSArray *arguments; @property (nonatomic, strong, readonly) NSInvocation *originalInvocation; @end

Implementation of AspectInfo correspondence

#pragma mark - AspectInfo @implementation AspectInfo @synthesize arguments = _arguments; - (ID) initWithInstance: (__unsafe_unretained ID) instance invocation: (NSInvocation * invocation) {NSCParameterAssert (instance); NSCParameterAssert (invocation); if (self = [super init]) {_instance = instance; _originalInvocation = invocation;} return self;} - (NSArray *) arguments evaluate {/ / Lazily arguments boxing is expensive. if (_arguments! = self.originalInvocation.aspects_arguments) {_arguments}; return _arguments;}

AspectInfo is inherited from NSObject, and follows the AspectInfo protocol. In its – (ID) initWithInstance: invocation: method, the AspectInfo that comes from outside the instance, and the original invocation to save the corresponding member variables in the class. (NSArray *) arguments method is a lazy loading, return the original invocation inside the aspects parameter array.

Aspects_arguments this getter method is how to achieve it? The author is implemented by adding a taxonomy for NSInvocation.

@interface NSInvocation (Aspects) - (NSArray *) aspects_arguments; @end

For the original NSInvocation class to add a Aspects classification, this classification is only one method, aspects_arguments, the return value is an array, which contains all the parameters of the current invocation.

Corresponding implementation

#pragma mark - NSInvocation (Aspects) @implementation NSInvocation (Aspects) - (NSArray * aspects_arguments) {NSMutableArray *argumentsArray = [NSMutableArray array]; for (NSUInteger IDX = 2; idx < self.methodSignature.numberOfArguments; idx++) {[argumentsArray addObject:[self aspect_argumentAtIndex:idx]} return?: NSNull.null]; [argumentsArray copy] @end;}

(NSArray *) aspects_arguments implementation is very simple, that is, a layer of for cycle, the methodSignature method to sign the parameters inside, are added to the array, and finally return to the array.

On the acquisition of all parameters of this method – (NSArray *) aspects_arguments method, there are 2 places need to be explained in detail. First, why the cycle from the beginning of 2, is how to achieve the internal [self aspect_argumentAtIndex:idx].

IOS how to achieve Aspect Oriented Programming (on)

Let’s start with 2.

Type Encodings as a supplement to the Runtime, the compiler encodes the return value of each method and the parameter type to a string, which is associated with the selector of the method. This encoding scheme is also very useful in other situations, so we can use the @encode compiler directive to get it. When given a type, @encode returns the string encoding of this type. These types can be the basic types such as int, pointers, or the structure, class, etc.. In fact, any type that can be used as an operating parameter of sizeof can be used for @encode ().

In the Type Programming Guide section of Objective-C Runtime, lists all types of encoding in the Objective-C Encoding. It is important to note that many of these types are the same as the type of code we use for archiving and distribution. But some can not be used in the archive.

Note: Objective-C does not support long double type. @encode (long double) returns D, the same as double.

IOS how to achieve Aspect Oriented Programming (on)

OC support for message forwarding and dynamic call, Objective-C Method Type information to the return value of combination of Type + Types encoding parameters “, also need to take into account the self
and _cmd the two implicit parameter:

- (void) tap; => "v@:" - (int) tapWithView: (double) pointx; => "i@: d"

In accordance with the above table, we can know that the encoded string, the top 3 are the return value Type, self implicit parameter Type @, _cmd implicit parameter Type:.

So from the beginning of the third is to participate in the senate.

Suppose we – (void) tapView: (UIView *) view atIndex: (NSInteger) index, for example, print methodSignature

(lldb) Po self.methodSignature < NSMethodSignature: 0x60800007df00> number of arguments = 4 frame size = 224 is special struct return NO return value: -------------------------------- type? Encoding (V)'v'flags modifiers frame {offset {} {} = 0 offset, adjust = 0, size = 0, size adjust = 0} memory {offset = 0, size = 0} argument 0: type encoding (@ --------------------------------) flags {isObject} modifiers frame {offset {}' @ offset = 0, adjust = 0, size = 8, size adjust = 0} memory {offset = 0, size = 8} argument 1: type encoding -------------------------------- (:) ':' flags modifiers frame {offset {} {} = 8, offset = 0 adjust. Size = 8, size adjust = 0} memory {offset = 0, size = 8} argument 2: data interchange for trade - type encoding (@) flags {isObject} modifiers frame {offset {} '@ offset = 16, adjust = 0, size = 8, size adjust = 0} memory {offset = 0, size = 8} argument 3: -------------------------------- type encoding (q)'q' flags isSigned} modifiers frame {offset {} {offset = 24, adjust = 0, size = 8 size, adjust = 0} memory {offset = 0, size = 8}

Number of arguments = 4, since there are 2 implicit parameters self and _cmd, plus the incorporation of view and index.

Argument Return value Zero One Two Three
MethodSignature V @ : @ Q

The first argument {offset = 0, offset = adjust = size = = size = 0}memory = {offset =, size = 0}, the return value here does not account for size adjust = frame. The second argument is self, frame = 0, offset = adjust = size = size = 0}memory = {offset = = size = 8} = adjust = {offset = 0. Since size = 8, the next frame of offset is 8, followed by the number of 16, and so on.

As for why here to pass 2, but also with the specific realization of aspect_argumentAtIndex.

Look at the specific implementation of aspect_argumentAtIndex. Thanks to the ReactiveCocoa team, this approach provides an elegant way to get the parameters of the method signature.

Thanks to the ReactiveCocoa team for / providing a generic solution for this. (ID) aspect_argumentAtIndex: (NSUInteger) index char *argType getArgumentTypeAtIndex:index] {const = [self.methodSignature; / / Skip const type qualifier. if (argType[0] = = _C_CONST) argType++; #define WRAP_AND_RETURN (type do) {type Val = 0; [self getArgument:& val atIndex: (NSInteger) index] return; @ (VAL);} while (0) if (StrCmp (argType, @encode (ID) StrCmp (argType) = 0 ||, @encode (Class)) = = 0) {__autoreleasing ID returnObj; [self getArgument:& returnObj atIndex: (NSInteger) index]; return returnObj;} else (StrCmp (argType, if @encode (SEL)) = = 0) {SEL selector = 0; [self getArgument:& selector index]; R atIndex: (NSInteger) Eturn NSStringFromSelector (selector);} else if (StrCmp (argType, @encode (Class)) = = 0) {__autoreleasing Class = theClass Nil; [self getArgument:& theClass atIndex: (NSInteger) index]; return theClass; Using this list will box the / number with the appropriate constructor, instead of the generic NSValue. if (else} StrCmp (argType, @encode (char)) = = 0) {WRAP_AND_RETURN} (char); else if (StrCmp (argType, @encode (int)) = = 0) {WRAP_AND_RETURN} (int); else if (StrCmp (argType, @encode (short)) = = 0) {WRAP_AND_RETURN} else (short); if (StrCmp (argType, @encode (long)) = = 0) {WRAP_AND_RETURN} (long); else if (StrCmp (argType, encode (long @ long)) = = 0) {WRAP_AND_RETURN (long long else if (StrCmp);} (argTyp E, @encode (unsigned) char) WRAP_AND_RETURN (unsigned = = 0) {char}); else if (StrCmp (argType, @encode (unsigned int)) = = 0) {WRAP_AND_RETURN} (unsigned int); else if (StrCmp (argType, @encode (unsigned short) = = 0) {WRAP_AND_RETURN (unsigned short) else (StrCmp);} if (argType, @encode (unsigned long)) = = 0) {WRAP_AND_RETURN} (unsigned long); else if (StrCmp (argType, @encode (unsigned long long)) = = 0) {WRAP_AND_RETURN (unsigned long long else (StrCmp);} if (argType, @encode (float)) = = 0) {WRAP_AND_RETURN} (float); else if (StrCmp (argType, @encode (double)) = = 0) {WRAP_AND_RETURN} (double); else if (StrCmp (argType @ encode (BOOL)) = = 0) {WRAP_AND_RETURN} (BOOL); else if (StrCmp (argType, @encode (bool)) = = 0) {WRAP_AND_RETURN} (BOOL); else if (StrCmp (argType, @encode (char *)) = = 0) {WRAP_AND_RETURN (const * char);} else if (StrCmp (argType, @encode (void (^) (void))) = = 0) {__unsafe_unretained ID block = nil; [self getArgument:& block atIndex: (NSInteger) index]; return [block copy];} else {NSUInteger valueSize = 0; NSGetSizeAndAlignment (argType, & valueSize, NULL); unsigned char valueBytes[valueSize]; [self getArgument: valueBytes atIndex: (NSInteger) index]; return [NSValue valueWithBytes:valueBytes objCType:argType] return nil #undef WRAP_AND_RETURN;};}

GetArgumentTypeAtIndex: this method is used to obtain the methodSignature method to specify the signature of the index type encoding string. This method is derived from the string is passed directly into the index value. For example, we pass in is 2, in fact, the string is the string of methodSignature corresponding to the third.

Since the zeroth bit is a function return value return value corresponding to the type, so the incoming 2, corresponding to the argument2 is encoding. So here we pass index = 2, is filtered out of the top 3 type encoding string, starting from the argument2 comparison. That’s why the loop starts at 2.

IOS how to achieve Aspect Oriented Programming (on) 

_C_CONST is a constant that is used to determine whether the encoding string is a CONST constant.

#define _C_ID #define _C_CLASS #define’#” @ ‘_C_SEL’: ‘#define _C_CHR’c’ #define _C_UCHR’C’#define _C_SHT’s’ #define _C_USHT’S’#define _C_INT’i’ #define _C_UINT’I’#define _C_LNG’l’ #define _C_ULNG’L’#define _C_LNG_LNG’q’ #define _C_ULNG_LNG’Q’#define _C_FLT’f’ #define _C_DBL’d’#define _C_BFLD’b’ #define _C_BOOL’B’#define _C_VOID’v’ #define _C_UNDEF ” #define _C_PTR ‘^’? #define _C_CHARPTR #define _C_ATOM #define _C_ARY_B'[‘% * #define _C_ARY_E #define _C_UNION_B (#define’]’ ” _C_UNION_E ” #define _C_STRUCT_B #define _C_STRUCT_E)'{”}’ #define _C_VECTOR ‘#define _C_CONST’r’ ‘!

Here’s the Type and OC Type is exactly the same, but here is a C char type.

#define WRAP_AND_RETURN (type) do {Val = 0; [self getArgument:& val atIndex: (NSInteger) index]; return @ (VAL);} while (0)

WRAP_AND_RETURN is a macro definition. This macro definition which calls the getArgument:atIndex: method is used in NSInvocation according to index to get the corresponding Argument, and finally return when the Val package into the object, go back.

In the following sections of the if – else judgment, there are a lot of string comparison function strcmp.

For example, StrCmp (argType, @encode (ID) = 0, argType) is a char, methodSignature content is taken out of the corresponding type encoding, and @encode (ID) is the same as type encoding. After comparing the StrCmp, if it is 0, the representative type is the same.

The following section is put into the judgment process parameters is returned, followed by judge ID, class, SEL, and then a big push basic types, char, int, short, long, long long, unsigned char, unsigned int, unsigned short, unsigned long, unsigned long long, float, double, BOOL bool, char *, these basic types will be packaged into the object returned by WRAP_AND_RETURN. Finally, the block and struct structures are judged, and the corresponding object is returned.

So the reference is returned to the array which is received. Hypothesis or above – (void) tapView: (UIView *) view atIndex: (NSInteger) index as an example, the implementation of the aspects_arguments, the array is installed inside the:

(< UIView: 0x7fa2e2504190; frame = (080; 41440); layer = < CALayer: 0x6080000347c0> > “, 1)

Summary, AspectInfo which is mainly NSInvocation information. NSInvocation packaging layer, such as parameter information.

4 AspectIdentifier

IOS how to achieve Aspect Oriented Programming (on) 

Tracks a single aspect. @interface / AspectIdentifier: NSObject + (instancetype) identifierWithSelector: (SEL) selector object: (ID) object options: (AspectOptions) options block: (ID) block error: (NSError) – (BOOL) error; invokeWithInfo: (id< AspectInfo> info; @property (nonatomic), assign @property (SEL) selector; nonatomic, strong) id block @property (nonatomic, strong); NSMethodSignature *blockSignature; @property (nonatomic, weak) id object @property (nonatomic, assign); AspectOptions options; @end

Corresponding implementation
#pragma mark – AspectIdentifier @implementation AspectIdentifier (instancetype) + identifierWithSelector: (SEL) selector object: (ID) object options: (AspectOptions) options block: (ID) block error: (NSError) {error NSCParameterAssert (block); NSCParameterAssert (selector); NSMethodSignature *blockSignature = aspect_blockMethodSignature (block, error); / / TODO: check signature compatibility, etc. if (! Aspect_isCompatibleBlockSignature (blockSignature, object, selector, error)) {return nil}; AspectIdentifier *identifier = nil; if (blockSignature) {identifier new] = [AspectIdentifier; identifier.selector = selector; identifier.block = block; identifier.blockSignature = blockSigna Ture; identifier.options = options; identifier.object = object; / / weak return identifier};} – (BOOL) invokeWithInfo: (id< AspectInfo> info) {NSInvocation *blockInvocation = [NSInvocation invocationWithMethodSignature: self.blockSignature]; NSInvocation *originalInvocation NSUInteger = info.originalInvocation; numberOfArguments = self.blockSignature.numberOfArguments; / / Be extra paranoid. We already check that on hook registration. if (numberOfArguments > originalInvocation.methodSignature.numberOfArguments (@ AspectLogError) {“Block has too many arguments. Not calling, info% @”); return NO;} / / The `self` of the block will be the AspectInfo. Optional. if (n UmberOfArguments > 1) {[blockInvocation setArgument:& info atIndex:1];} void *argBuf for (NSUInteger = NULL; IDX = 2; idx < numberOfArguments; idx++) {const char *type = [originalInvocation.methodSignature getArgumentTypeAtIndex:idx]; NSUInteger argSize; NSGetSizeAndAlignment (type, & argSize, NULL); if (! (argBuf = (argBuf, reallocf argSize))) {AspectLogError (“Failed to allocate memory @ for block invocation.”); return NO; [originalInvocation getArgument:argBuf atIndex:idx] [blockInvocation setArgument:argBuf}}; atIndex:idx]; [blockInvocation invokeWithTarget:self.block]; if (argBuf! = NULL) {free} (argBuf); return YES;} – (NSString * DES) Cription [NSString stringWithFormat:@ {return < SEL:%@ object:%@ options:%tu% @:%p, block:%@ (#%tu args &gt); “self.class, self, NSStringFromSelector, (self.selector), self.object, self.options, self.block, self.blockSignature.numberOfArguments];} – {(BOOL) remove return aspect_remove (self, NULL);} @end
The aspect_blockMethodSignature method is called in the instancetype method.

Static NSMethodSignature *aspect_blockMethodSignature (ID block, NSError **error) {AspectBlockRef (layout = __bridge * block; if (void) (layout-> flags! & AspectBlockFlagsHasSignature)) {NSString *description = [NSString stringWithFormat:@ The block doesn’t contain a type% @ signature., block]; AspectError (AspectErrorMissingBlockSignature, description); return nil void;} *desc = layout-> descriptor; desc = 2 * sizeof (unsigned long int); if (layout-> flags & AspectBlockFlagsHasCopyDisposeHelpers) {desc = 2 * sizeof (void *);} if (desc!) {NSString *description = [NSString stringWithFormat:@ The block doesn’t has a type% @ signature., AspectError (block]; AspectErr OrMissingBlockSignature, description); return nil;} char *signature = (* const * char * DESC); return [NSMethodSignature signatureWithObjCTypes:signature];}

The purpose of this aspect_blockMethodSignature is to pass the incoming AspectBlock into a NSMethodSignature method signature.

The structure of AspectBlock is as follows

Typedef struct _AspectBlock __unused Class AspectBlockFlags {isa; flags; __unused int reserved; void (__unused *invoke) (struct _AspectBlock, *block,...); struct long int unsigned {unsigned reserved; 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};

Here defines a block type that is used internally by Aspects. Block on the system is very familiar with the students will feel like a two. Unfamiliar can look at my previous analysis of Block article. In this article, Clang is used to convert the Block into a structure that is similar to the block defined here.

IOS how to achieve Aspect Oriented Programming (on)

Understanding the structure of the AspectBlock, then look at the aspect_blockMethodSignature function is more clear.

AspectBlockRef = layout (__bridge * void) block; if (! (layout-&gt flags; & AspectBlockFlagsHasSignature)) {NSString *description = [NSString stringWithFormat:@ The block doesn’t contain a type% @ signature., block]; AspectError (AspectErrorMissingBlockSignature, description); return nil;}

AspectBlockRef layout (__bridge = void * block), because of the block to achieve a similar, so here to put into block forced conversion to type AspectBlockRef, and then determine whether there is a sign of AspectBlockFlagsHasSignature, if not, report does not contain the method signature error.

Note that the incoming block is global

(__NSGlobalBlock) __NSGlobalBlock = {NSBlock = {NSObject = {isa =}}}

Void *desc = layout-> descriptor; desc = 2 * sizeof (unsigned long int); if (layout-> flags & AspectBlockFlagsHasCopyDisposeHelpers) {desc = 2 * sizeof (void *);} if (desc!) {NSString *description = [NSString stringWithFormat:@ The block doesn’t has a type% @ signature., block]; AspectError (AspectErrorMissingBlockSignature, description); return nil;}

Desc is the original block corresponding to the inside of the descriptor pointer. The descriptor pointer moves down to 2 unsigned int locations and points to the address of the copy function, if you include the Copy and Dispose functions, then move down to the size of the 2 (void). Then the pointer must move to the location of the const char signature. If desc does not exist, it will also be reported that the block does not contain a method signature.

Const char *signature = (* const * char * DESC); return [NSMethodSignature signatureWithObjCTypes:signature];
Here, there is a way to ensure that the signature, and the existence of. Finally, call the NSMethodSignature signatureWithObjCTypes method to return the method signature.

Give an example of how the signature of the aspect_blockMethodSignature generated method is.

[UIView aspect_hookSelector:@selector (UIView:atIndex:) withOptions:AspectPositionAfter usingBlock:^ (id< AspectInfo> aspects, UIView *view, NSInteger index) {NSLog (@ "button click"%ld, index error:nil]);};

Const char *signature finally gets the string like this

(const char *) signature = 0x0000000102f72676 “v32@? 0@/” < AspectInfo> “8@/” UIView/ “16q24”

V32@? 0@ "< AspectInfo>" 8@ "UIView" 16q24 is Block

^ (id< AspectInfo> aspects, UIView *view, NSInteger index) {}

Corresponding Type. Void returns the value of Type is V, 32 is offset, @? Block corresponds to the Type, @ < AspectInfo> is the first parameter, @ UIView is the second parameter, NSInteger corresponds to the Type is the q.

Each Type followed by the number of their respective corresponding offset. Print out the final converted NSMethodSignature.

< NSMethodSignature: 0x600000263dc0> number of arguments = 4 frame size = 224 is special struct return NO return value: -------------------------------- type? Encoding (V)'v'flags modifiers frame {offset {} {} = 0 offset, adjust = 0, size = 0, size adjust = 0} memory {offset = 0, size = 0} argument 0: type encoding -------------------------------- (@)' @ 'flags {isObject, isBlock} modifiers? Frame {offset offset {} = 0, adjust = 0, size = 8, size adjust = 0} memory {offset = 0, size = 8} argument 1: type encoding (--------------------------------' @ @) "< AspectInfo> ' Flags {isObject} modifiers frame {offset offset {} = 8, adjust = 0, size = 8, size adjust = 0} memory {offset = 0, size = 8} conforms to protocol'AspectInfo'argument 2: type encoding (--------------------------------' @ @) "UIView" flags' {isObject} modifiers frame {offset offset {} = 16, adjust = 0, size = 8 size, adjust = 0} memory {offset = 0, size = 8} class'DLMenuView'argument 3: -------------------------------- type encoding (q)'q' flags {isSigned} modifiers frame {offset} {offset = 24, adjust = 0, size = 8, size adjust = 0} memory {offset = 0, size = 8}

Back to the AspectIdentifier continue to look at the instancetype method, access to the incoming block method signature, also called the aspect_isCompatibleBlockSignature method.

Static BOOL aspect_isCompatibleBlockSignature (NSMethodSignature *blockSignature, ID object, SEL selector, NSError **error) {NSCParameterAssert (blockSignature); NSCParameterAssert (object); NSCParameterAssert (selector); BOOL signaturesMatch = YES; NSMethodSignature = *methodSignature [[object class] instanceMethodSignatureForSelector:selector] if (blockSignature.numberOfArguments; > methodSignature.numberOfArguments) {signaturesMatch = NO;}else {if (blockSignature.numberOfArguments > 1 const *blockType = [blockSignature char) {getArgumentTypeAtIndex:1]; if (blockType[0]! = ‘@’) {signaturesMatch = NO;}} / / Argument 0 Is self/block, argument 1 is SEL or id< AspectInfo> We start comparing at. Argument The block can have less 2 / arguments than the method, that’s ok. 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. if (methodType blockType methodType[0] || ||!!! = blockType[0]) {signaturesMatch = NO; break}}};} if {NSStr (signaturesMatch!) Ing *description = [NSString stringWithFormat:@ Block signature doesn’t match%@.% @ “, blockSignature, methodSignature]; AspectError (AspectErrorIncompatibleBlockSignature, description); return NO;} return YES;}

The role of this function is to replace the method we want to replace the block and the original method to be compared. How to compare? Compare the signature of both methods.

Selector is the original method.

If (blockSignature.numberOfArguments > methodSignature.numberOfArguments) {signaturesMatch = NO;}else {if (blockSignature.numberOfArguments > 1) {const char *blockType = [blockSignature getArgumentTypeAtIndex:1]; if (blockType[0]! = '@') {signaturesMatch = NO;}}

Compare the number of parameters of the method signature is equal, inequality is not sure, signaturesMatch = NO. If the same number of parameters, and then compare the method we want to replace the first argument is not _cmd, the corresponding Type is @, if not, is not match, so signaturesMatch = NO. If the above two are satisfied, signaturesMatch = YES, then enter the following more stringent contrast.

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. if (methodType blockType methodType[0] || ||!!! = blockType[0]) {signaturesMatch = NO; break;}}}

Here the cycle starts from 2. An example is given to illustrate why the comparison is made from the second place. Or use the previous example.

[UIView aspect_hookSelector:@selector (UIView:atIndex:) withOptions:AspectPositionAfter usingBlock:^ (id< AspectInfo> aspects, UIView *view, NSInteger index) {NSLog (@ “button click”%ld, index error:nil]);};

Here I would like to replace the original method is UIView:atIndex:, then the corresponding Type is v@: @q. Based on the above analysis, the blockSignature here is a Type that was previously called out, and should be v@ @ @ < AspectInfo> @ @ UIView @ Q.

MethodSignature and blockSignature return are void, so the corresponding are V, value. MethodSignature argument 0 is an implicit parameter self, so the corresponding is @. BlockSignature argument 0 is block, so the corresponding is @?. MethodSignature argument 1 is an implicit parameter _cmd, so the corresponding:. BlockSignature argument 1 is < AspectInfo> so the corresponding is @ < AspectInfo>”. Starting from argument 2 is a list of parameters that may appear different after the method signature.

Last

(if! SignaturesMatch) {NSString *description = [NSString stringWithFormat:@ Block signature doesn’t match%@.% @ “, blockSignature, methodSignature]; AspectError (AspectErrorIncompatibleBlockSignature, description); return NO;}

Argument Return value Zero One Two Three
MethodSignature V @ : @ Q
BlockSignature V @? @ @ < AspectInfo>” @ @ UIView” Q

If the above comparison signaturesMatch are NO, then throw error, Block can not match the method signature.

AspectIdentifier *identifier = nil; if (blockSignature) {identifier new] = [AspectIdentifier; identifier.selector = selector; identifier.block = block; identifier.blockSignature = blockSignature; identifier.options = options; identifier.object = object;} / / weak return identifier;

If the match is successful, blockSignature will be assigned to AspectIdentifier. This is why there is a separate property AspectIdentifier in NSMethodSignature.

AspectIdentifier there’s another way invokeWithInfo.

Be extra paranoid. We already check / that on hook registration. if (numberOfArguments > originalInvocation.methodSignature.numberOfArguments) {AspectLogError ("Block has too many @ arguments. Not, info calling% @"); return NO;}

Note also written clearly, this judgment is written in obsessive-compulsive disorder, where the block parameter is not greater than the original method, the number of parameters within the signature.

The `self` of the block will / be the AspectInfo. Optional. if (numberOfArguments > 1) {[blockInvocation setArgument:& info atIndex:1];}

Save AspectInfo to blockInvocation.

Void *argBuf for (NSUInteger = NULL; IDX = 2; idx < numberOfArguments; idx++) {const char *type = [originalInvocation.methodSignature getArgumentTypeAtIndex:idx]; NSUInteger argSize; NSGetSizeAndAlignment (type, & argSize, NULL); if (! (argBuf = reallocf (argBuf, argSize))) {AspectLogError (“to allocate memory @ Failed for block invocation.”); return NO; [originalInvocation getArgument:argBuf atIndex:idx] [blockInvocation}}; setArgument:argBuf atIndex:idx]; [blockInvocation invokeWithTarget:self.block];

This is the cycle of the originalInvocation out of the parameters, assigned to the argBuf, and then assigned to the blockInvocation inside. The reason why the loop has started since 2 has already been said. Finally assign self.block to blockInvocation's Target.

IOS how to achieve Aspect Oriented Programming (on)

Summary, AspectIdentifier is a slice of the specific content of Aspect. Which will contain a single Aspect specific information, including the implementation of the timing, to implement the specific information required by the block: including method signatures, parameters, etc.. AspectIdentifier initialization process is the essence of our incoming block packaged into AspectIdentifier.

5 AspectsContainer

IOS how to achieve Aspect Oriented Programming (on)

Tracks all aspects for an object/class. / @interface AspectsContainer: NSObject – (void) addAspect: (AspectIdentifier *) aspect withOptions: (AspectOptions) – (BOOL) injectPosition; removeAspect: (aspect; ID) – (BOOL) hasAspects (atomic, copy); @property NSArray *beforeAspects; @property (atomic copy, NSArray *insteadAspects @property (atomic); NSArray *afterAspects; @end, copy)

Corresponding implementation
#pragma mark – AspectsContainer @implementation AspectsContainer – hasAspects return self.beforeAspects.count (BOOL) {&gt 0; self.insteadAspects.count &gt self.afterAspects.count || ||; 0 > 0;} – (void) addAspect: (AspectIdentifier *) aspect withOptions: (AspectOptions) options NSParameterAssert (aspect) {NSUInteger = options& position; AspectPositionFilter; switch (position) {case AspectPositionBefore: self.beforeAspects = [(self.beforeAspects?: @[]) arrayByAddingObject:aspect]; break case; AspectPositionInstead: self.insteadAspects = [(self.insteadAspects?: @[]) arrayByAddingObject:aspect]; break case; AspectPositionAfter: self.afterAspects = [(self.afterAspects?: @[]) arrayByAddingObje Ct:aspect]; break;}} – (BOOL) removeAspect: (ID) aspect (NSString *aspectArrayName in @[NSStringFromSelector {for (@selector (beforeAspects)), NSStringFromSelector (@selector (insteadAspects)), NSStringFromSelector (@ selector (afterAspects)])) {NSArray *array = [self valueForKey:aspectArrayName]; NSUInteger index = [array indexOfObjectIdenticalTo:aspect] (array & if &; index! = NSNotFound) {NSMutableArray *newArray = [NSMutableArray arrayWithArray:array]; [newArray removeObjectAtIndex:index]; [self setValue:newArray forKey:aspectArrayName]; return YES;}} return NO;} – {return (NSString *) description [NSString stringWithFormat:@ <%p, before:%@:% @ instead:%@, after:%@> self.class, self, self.beforeAspects, self.insteadAspects, self.afterAspects], @end};
AspectsContainer better understanding. AddAspect will be cut in accordance with the timing of the slice Aspects into the corresponding array. RemoveAspects will loop remove all Aspects. HasAspects to determine whether there is Aspects.

AspectsContainer is an object or class of all Aspects containers. There are two kinds of containers.

It is worth noting that the array is modified by Atomic. As for Atomic, it is important to note that by default, the method synthesized by the compiler ensures its atomicity (Atomicity) through the locking mechanism. If the property has a nonatomic property, no synchronization locks are required.

6 AspectTracker

IOS how to achieve Aspect Oriented Programming (on)

@interface AspectTracker: NSObject – (ID) initWithTrackedClass: (Class) trackedClass (nonatomic, strong); @property Class trackedClass; @property (nonatomic, readonly) NSString *trackedClassName @property (nonatomic, strong); NSMutableSet *selectorNames; @property (nonatomic, strong) NSMutableDictionary *selectorNamesToSubclassTrackers; (void) – addSubclassTracker: (AspectTracker * hookingSelectorName: * NSString (subclassTracker) selectorName); – (void) removeSubclassTracker: (AspectTracker * subclassTracker) hookingSelectorName: (NSString * selectorName); – (BOOL) subclassHasHookedSelectorName: (NSString * selectorName); – (NSSet *) subclassTrackersHookingSelectorName: (* NSString) selectorName; @end

Corresponding implementation
@implementation AspectTracker – (ID) initWithTrackedClass: (Class trackedClass) {if (self = [super init]) {_trackedClass = trackedClass; _selectorNames = [NSMutableSet _selectorNamesToSubclassTrackers = [NSMutableDictionary new]; new] return self;};} – (BOOL) subclassHasHookedSelectorName: (NSString * selectorName) {return} – self.selectorNamesToSubclassTrackers[selectorName]! = nil; addSubclassTracker: (AspectTracker (void) subclassTracker hookingSelectorName: (NSString) * * selectorName) {NSMutableSet *trackerSet = self.selectorNamesToSubclassTrackers[selectorName]; if (! TrackerSet) {trackerSet new] = [NSMutableSet; self.selectorNamesToSubclassTrackers[selectorName] = tracke RSet [trackerSet addObject:subclassTracker];};} – (void) removeSubclassTracker: (AspectTracker * subclassTracker) hookingSelectorName: (NSString * selectorName) {NSMutableSet *trackerSet = self.selectorNamesToSubclassTrackers[selectorName]; [trackerSet removeObject:subclassTracker]; if (trackerSet.count = = 0) {[self.selectorNamesToSubclassTrackers removeObjectForKey:selectorName]};} – (NSSet *) subclassTrackersHookingSelectorName: (NSString * selectorName) {NSMutableSet *hookingSubclassTrackers = [NSMutableSet (new]; for AspectTracker *tracker in self.selectorNamesToSubclassTrackers[selectorName]) {if ([tracker.selectorNames containsObject:selectorName]) {[hookingSubclassTrackers AddObject:tracker] [hookingSubclassTrackers unionSet:[tracker subclassTrackersHookingSelectorName:selectorName]];}}; return hookingSubclassTrackers;} – {return (NSString *) trackedClassName NSStringFromClass (self.trackedClass);} – (NSString * description) {return [NSString stringWithFormat:@ < trackedClass::% @% @% @ selectorNames:%@, subclass, selector, names:, self.class,%@> “self, NSStringFromClass (self.trackedClass). Self.selectorNames, self.selectorNamesToSubclassTrackers.allKeys]; @end}
AspectTracker this class is used to track the class to be hook. TrackedClass is a trace class. TrackedClassName is the class name of the trace class. SelectorNames is a NSMutableSet, which will record the name of the method to be replaced by the hook, with NSMutableSet in order to prevent repeat replacement method. SelectorNamesToSubclassTrackers is a dictionary, key is hookingSelectorName, value is filled with AspectTracker NSMutableSet.

The addSubclassTracker method is to add AspectTracker to the corresponding selectorName set. The removeSubclassTracker method removes the AspectTracker from the corresponding selectorName collection. The subclassTrackersHookingSelectorName method is a collection and investigation, pass a selectorName, through the recursive search, find out all the selectorName set, set finally put these together as return values.

Four. Preparation work before Aspects hook

IOS how to achieve Aspect Oriented Programming (on)

Aspects library on the two functions, one for the class, one is for instance.

+ (id< AspectToken> aspect_hookSelector:) (SEL) selector withOptions: (AspectOptions) options usingBlock: (ID) block error: (NSError) error {return aspect_add ((ID) self, selector, options, block, error);} – (id& lt; AspectToken> aspect_hookSelector:) (SEL) selector withOptions: (AspectOptions) options usingBlock: (ID) block error: (NSError) error {return aspect_add (self, selector, options, block, error);}

The implementation of the two methods are called the same method aspect_add, but the parameters are different. So we just have to start with aspect_add.

Aspect_hookSelector: (SEL) selector withOptions: (AspectOptions) options usingBlock: (ID) block error: (NSError) error aspect_add (self, selector, options, block, error) – aspect_performLocked – aspect_isSelectorAllowedAndTrack; had – aspect_prepareClassAndHookSelector

Function call stack. Start from 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; aspect_performLocked (if ^{(aspect_isSelectorAllowedAndTrack (self, selector, options, error) {AspectsContainer = aspect_getContainerForObject (*aspectContainer) self selector; identifier = [AspectIdentifier), identifierWithSelector:selector object:self options:options block:block error:error]; if (identifier) {[aspectContainer addAspect:identifier withOptions:options]; Modify the class to allow message / / interception. Aspect_prepareClassAndHookSelector (self, selector, error);}}}; return identifier;}

Aspect_add function of a total of 5 parameters, the first parameter is self, selector is outside the need to pass the hook SEL, options is a slice of the time, block is the implementation of the slicing method, the final error is wrong.

Aspect_performLocked is a spin lock. Spin lock is a lock of high efficiency, much more efficient than @synchronized.

Static void aspect_performLocked (dispatch_block_t block) {static OSSpinLock aspect_lock = OS_SPINLOCK_INIT; OSSpinLockLock (& aspect_lock); (block); OSSpinLockUnlock (& aspect_lock);}

If you do not understand the 8 locks in iOS, you can see the following two articles

IOS common knowledge points (three): Lock
in-depth understanding of the lock in iOS development

IOS how to achieve Aspect Oriented Programming (on) 

But it is possible to spin lock problem:
if a low priority thread to acquire the lock and access to a shared resource, then a high priority thread attempts to obtain the lock, it will be in spin lock (busy-wait) and other busy state so as to occupy a lot of CPU. At this point, the low priority thread can not compete with the high priority thread CPU time, resulting in the task can not be completed, unable to release lock. OSSpinLock is no longer safe

The problem with OSSpinLock is that there is a potential risk of deadlock if access to the thread is not the same priority.

This is considered to be the same priority thread, so OSSpinLock ensures the thread safety. That is to say, aspect_performLocked is a thread safe protection for block.

Now the aspect_isSelectorAllowedAndTrack function and the aspect_prepareClassAndHookSelector function are left.

Then look at the aspect_isSelectorAllowedAndTrack function implementation process.

Static NSSet *disallowedSelectorList; static dispatch_once_t PRED; dispatch_once (& PRED, disallowedSelectorList [NSSet setWithObjects:@ ^{= “retain”, “release”, @ @ @ “autorelease”, “forwardInvocation:”, nil];});

The first definition of a NSSet, which is a “blacklist” is not allowed hook function name. Retain, release, autorelease, forwardInvocation: are not allowed to be hook.

NSString *selectorName = NSStringFromSelector (selector); if ([disallowedSelectorList containsObject:selectorName]) {NSString *errorDescription = [NSString stringWithFormat:@ Selector is selectorName];% @ blacklisted., AspectError (AspectErrorSelectorBlacklisted, errorDescription); return NO;}

When the name of the function named selector is detected, the name of the function is immediately reported.

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 again if you want to slice dealloc, slicing time can only be before dealloc, if not AspectPositionBefore, but also the wrong.

If ([self respondsToSelector:selector] & &!! [self.class instancesRespondToSelector:selector]) {NSString *errorDesc = [NSString stringWithFormat:@ Unable to find selector -[%@%@]. “(self.class), NSStringFromClass, selectorName]; AspectError (AspectErrorDoesNotRespondToSelector, errorDesc); return NO;}

When selector is not in the blacklist, if the slice is dealloc, and selector before it. It is time to judge whether the method exists. If self and self.class can not find the inside of the selector, the error will not find the method.

If (class_isMetaClass (object_getClass (self))) {Class Klass = [self class]; NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict (Class = [self); currentClass class]; AspectTracker *tracker = swizzledClassesDict[currentClass]; if ([tracker subclassHasHookedSelectorName:selectorName]) {NSSet *subclassTracker = [tracker subclassTrackersHookingSelectorName:selectorName]; NSSet *subclassNames [subclassTracker valueForKey:@ = “trackedClassName”]; NSString *errorDescription = [NSString stringWithFormat:@ Error: already hooked subclasses: A%@.% @ method can only be hooked once per class hierarchy., selectorName, subclassNames]; AspectErr Or (AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription); return NO;}

Class_isMetaClass before the judgment is not a metaclass. The judgment is allowed inside can judge metaclass substitution method.

SubclassHasHookedSelectorName will determine whether the current tracker subclass contains selectorName. Because a method can only be hook once in a class hierarchy. If you already have a tracker inside, then it will be wrong.

Do {tracker = swizzledClassesDict[currentClass]; if ([tracker.selectorNames containsObject:selectorName]) {if (Klass = = currentClass) {/ / Already modified and topmost! Return YES NSString *errorDescription [NSString stringWithFormat:;} = @ "% @ Error: already hooked in A can only be%@. method hooked once per class hierarchy., selectorName, NSStringFromClass (currentClass (AspectError)]; AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription); return NO;}} (while (currentClass = class_getSuperclass (currentClass)));

In this DO-WHILE loop, currentClass = class_getSuperclass (currentClass) this judgment will start from the currentClass superclass, has been looking up until this class is the root class NSObject.

CurrentClass AspectTracker = Klass; *subclassTracker = nil; do {tracker = swizzledClassesDict[currentClass]; if (tracker! = [[AspectTracker) {tracker alloc] initWithTrackedClass:currentClass]; swizzledClassesDict[(id< NSCopying> currentClass] = tracker;}) if (subclassTracker) {[tracker} else {addSubclassTracker:subclassTracker hookingSelectorName:selectorName]; [tracker.selectorNames addObject:selectorName];} / / All superclasses get marked as having a subclass that is modified. (subclassTracker = tracker;}while (currentClass = class_getSuperclass (currentClass);

After the legality of the above hook and the class method does not allow for the replacement of the check, so that you can record the information to be hook, with AspectTracker tags. Once the subclass is changed, the parent class also needs to be tagged together. DO-WHILE termination condition or currentClass = class_getSuperclass (currentClass).

The above method is to determine the legitimacy of the class of hook code.

If it is not as long as it is not the hook class "retain", "release", "autorelease", "forwardInvocation:" 4 methods, hook and "dealloc" method of time must be before, and selector can be found, then the method can be hook.

After the selector can be checked by the legality of hook, it is necessary to obtain or create a AspectsContainer container.

Loads or creates the aspect container. / static AspectsContainer *aspect_getContainerForObject (NSObject *self, SEL selector) {NSCParameterAssert (self); SEL aliasSelector = aspect_aliasForSelector (selector); AspectsContainer * aspectContainer = objc_getAssociatedObject (self, aliasSelector); if (! AspectContainer) {aspectContainer = [AspectsContainer new]; objc_setAssociatedObject (self, aliasSelector, aspectContainer, OBJC_ASSOCIATION_RETAIN); return aspectContainer};}

Before you read or create AspectsContainer, the first step is to mark selector.

Static SEL aspect_aliasForSelector (SEL selector) {NSCParameterAssert (selector); return NSSelectorFromString (“[AspectsMessagePrefix stringByAppendingFormat:@ _%@”, NSStringFromSelector (selector)]);}

Defines a constant string in the global code

Static NSString AspectsMessagePrefix = @ @ *const aspects_”;

Use this string to mark all the selector, with the prefix "aspects_"". And then get the corresponding AssociatedObject related objects, if you can not get, to create an associated object. Selector finally get the "aspects_" prefix, the corresponding aspectContainer.

After getting the aspectContainer, you can begin to prepare some of the information we want to hook method. The information is installed in AspectIdentifier, so we need to create a new AspectIdentifier.

Call the AspectIdentifier method of instancetype to create a new AspectIdentifier

(instancetype) identifierWithSelector: (SEL) selector object: (ID) options: options (AspectOptions) block: object (ID) block error: (NSError *) error

In this instancetype method, only one case will fail, that is, the aspect_isCompatibleBlockSignature method returns NO. To return to NO means that we have to replace the method block and the original method to be replaced, and the signature of both methods is not consistent. (this function has been explained above, no longer repeat here). After the signature match is successful, it creates a AspectIdentifier.

[aspectContainer addAspect:identifier withOptions:options];

The aspectContainer container will add it to the container. After the completion of the container and AspectIdentifier initialization, you can begin to prepare for the hook. Options options are added to the container by the beforeAspects, insteadAspects, afterAspects these three arrays

The class to allow message / Modify interception. aspect_prepareClassAndHookSelector (self, selector, error);

To sum up, what did aspect_add do to prepare for the work:

IOS how to achieve Aspect Oriented Programming (on)

Because Jane book a single article limit, but can only be split into 2 parts, the lower part of the see.

  1. First call aspect_performLocked, the use of spin locks to ensure the safety of the entire operation of the thread
  2. Then call aspect_isSelectorAllowedAndTrack to pass on the parameters of the strong check to ensure the legitimacy of the parameters.
  3. Then create the AspectsContainer container, use the AssociatedObject object to dynamically add to the NSObject classification as an attribute.
  4. Then by reference selector, option, create AspectIdentifier instance. AspectIdentifier contains a single Aspect specific information, including the timing of the implementation of the specific information needed to perform block.
  5. Then add the specific information of a single AspectIdentifier to the AspectsContainer container. Options options are added to the container by the beforeAspects, insteadAspects, afterAspects these three arrays.
  6. Finally call prepareClassAndHookSelector to prepare hook.
In the Runtime hospital after two days of hospitalization, analysis of the realization of the principle of AOP. After discharge, found that the Aspect library has not been analyzed in detail, so there is this article, today to talk about how to achieve Aspect iOS Oriented Programming.

由于简书单篇文章字数限制,无奈只能拆成2篇,下部分见下篇。