Aspects源碼解析

A delightful, simple library for aspect oriented programming

  • 關(guān)鍵字:面向切片編程、OC動(dòng)態(tài)性、消息轉(zhuǎn)發(fā)、類型編碼、Swizzle...

  • 使用場(chǎng)景:

    • 1.統(tǒng)一處理邏輯
    • 2.在不改變?cè)创a的情況下,插入代碼(如無(wú)侵染更改第三方庫(kù)代碼,干一些壞壞的事情)

Aspects只有一個(gè)類文件,非常輕量級(jí),在實(shí)現(xiàn)的思路上和JSPatch差不多。都主要用到OC的消息轉(zhuǎn)發(fā),最終都交給ForwardInvocation實(shí)現(xiàn)。二者很多地方有異曲同工之妙。

基本原理

我們知道 OC 是動(dòng)態(tài)語(yǔ)言,我們執(zhí)行一個(gè)函數(shù)的時(shí)候,其實(shí)是在發(fā)一條消息:[receiver message],這個(gè)過(guò)程就是根據(jù) message 生成 selector,然后根據(jù) selector 尋找指向函數(shù)具體實(shí)現(xiàn)的指針IMP,然后找到真正的函數(shù)執(zhí)行邏輯。這種處理流程給我們提供了動(dòng)態(tài)性的可能,試想一下,如果在運(yùn)行時(shí),動(dòng)態(tài)的改變了 selector 和 IMP 的對(duì)應(yīng)關(guān)系,那么就能使得原來(lái)的[receiver message]進(jìn)入到新的函數(shù)實(shí)現(xiàn)了。

還是先來(lái)普及一下:

OC上,每個(gè)類都是這樣一個(gè)結(jié)構(gòu)體:

struct objc_class {
  struct objc_class * isa;
  const char *name;
  ….
  struct objc_method_list **methodLists; /*方法鏈表*/
};

其中 methodList 方法鏈表里存儲(chǔ)的是 Method類型:

typedef struct objc_method *Method;
typedef struct objc_ method {
  SEL method_name;
  char *method_types;
  IMP method_imp;
};

Method 保存了一個(gè)方法的全部信息,包括 SEL 方法名,type各參數(shù)和返回值類型,IMP該方法具體實(shí)現(xiàn)的函數(shù)指針。

通過(guò) Selector 調(diào)用方法時(shí),會(huì)從methodList 鏈表里找到對(duì)應(yīng)Method進(jìn)行調(diào)用,這個(gè) methodList上的的元素是可以動(dòng)態(tài)替換的,可以把某個(gè)Selector對(duì)應(yīng)的函數(shù)指針IMP替換成新的,也可以拿到已有的某個(gè) Selector 對(duì)應(yīng)的函數(shù)指針IMP,讓另一個(gè)Selector 跟它對(duì)應(yīng),Runtime提供了一些接口做這些事。

比如:

static void viewDidLoadIMP (id slf, SEL sel) {
 // Custom Code
}

Class cls = NSClassFromString(@"UIViewController");
SEL selector = @selector(viewDidLoad);
Method method = class_getInstanceMethod(cls, selector);

//獲得viewDidLoad方法的函數(shù)指針
IMP imp = method_getImplementation(method)

//獲得viewDidLoad方法的參數(shù)類型
char *typeDescription = (char *)method_getTypeEncoding(method);

//新增一個(gè)ORIGViewDidLoad方法,指向原來(lái)的viewDidLoad實(shí)現(xiàn)
class_addMethod(cls, @selector(ORIGViewDidLoad), imp, typeDescription);

//把viewDidLoad IMP指向自定義新的實(shí)現(xiàn)
class_replaceMethod(cls, selector, viewDidLoadIMP, typeDescription);

這樣就把 UIViewController-viewDidLoad方法給替換成我們自定義的方法,APP里調(diào)用 UIViewControllerviewDidLoad 方法都會(huì)去到上述 viewDidLoadIMP 函數(shù)里,在這個(gè)新的IMP函數(shù)里調(diào)用新增的方法,就實(shí)現(xiàn)了替換viewDidLoad 方法,同時(shí)為 UIViewController新增了個(gè)方法 -ORIGViewDidLoad指向原來(lái)viewDidLoadIMP, 可以通過(guò)這個(gè)方法調(diào)用到原來(lái)的實(shí)現(xiàn)。

.Aspect要的是實(shí)現(xiàn)一個(gè)通用的IMP,任意方法任意參數(shù)都可以通過(guò)這個(gè)IMP中轉(zhuǎn)。上面講的都是針對(duì)某一個(gè)方法的替換,但如果這個(gè)方法有參數(shù),怎樣把參數(shù)值傳給我們新的 IMP 函數(shù)呢?例如 UIViewController-viewDidAppear:方法,調(diào)用者會(huì)傳一個(gè) Bool值,我們需要在自己實(shí)現(xiàn)的IMP(上述的viewDidLoadIMP)上拿到這個(gè)值,怎樣能拿到?如果只是針對(duì)一個(gè)方法寫IMP,是可以直接拿到這個(gè)參數(shù)值的。如何達(dá)到通用的效果呢?

如何實(shí)現(xiàn)方法替換

  • va_list實(shí)現(xiàn)(一次取出方法的參數(shù))

這段代碼摘至JSPatch:

static void commonIMP(id slf, ...)
  va_list args;
  va_start(args, slf);
  NSMutableArray *list = [[NSMutableArray alloc] init];
  NSMethodSignature *methodSignature = [cls  instanceMethodSignatureForSelector:selector];
  NSUInteger numberOfArguments = methodSignature.numberOfArguments;
  id obj;
  for (NSUInteger i = 2; i < numberOfArguments; i++) {
      const char *argumentType = [methodSignature getArgumentTypeAtIndex:i];
      switch(argumentType[0]) {
          case 'i':
              obj = @(va_arg(args, int));
              break;
          case 'B':
              obj = @(va_arg(args, BOOL));
              break;
          case 'f':
          case 'd':
              obj = @(va_arg(args, double));
              break;
          …… //其他數(shù)值類型
          default: {
              obj = va_arg(args, id);
              break;
          }
      }
      [list addObject:obj];
  }
  va_end(args);
  [function callWithArguments:list];
}

這樣無(wú)論方法參數(shù)是什么,有多少個(gè),都可以通過(guò)va_list的一組方法一個(gè)個(gè)取出來(lái),組成 NSArray 。很完美地解決了參數(shù)的問(wèn)題,一直運(yùn)行正常,但是在arm64va_list 的結(jié)構(gòu)改變了,導(dǎo)致無(wú)法上述這樣取參數(shù)。

所以需要找到另一種方法。

  • ForwardInvocation實(shí)現(xiàn)

    • 看圖說(shuō)話

從上面我們可以發(fā)現(xiàn),在發(fā)消息的時(shí)候,如果 selector 有對(duì)應(yīng)的 IMP ,則直接執(zhí)行,如果沒(méi)有,oc給我們提供了幾個(gè)可供補(bǔ)救的機(jī)會(huì),依次有 resolveInstanceMethodforwardingTargetForSelector、forwardInvocation。

Aspects之所以選擇在 forwardInvocation 這里處理是因?yàn)?,這幾個(gè)階段特性都不太一樣:

  • resolvedInstanceMethod: 適合給類/對(duì)象動(dòng)態(tài)添加一個(gè)相應(yīng)的實(shí)現(xiàn),
  • forwardingTargetForSelector:適合將消息轉(zhuǎn)發(fā)給其他對(duì)象處理,
  • forwardInvocation: 是里面最靈活,最能符合需求的。

因此 Aspects的方案就是,對(duì)于待 hookselector,將其指向 objc_msgForward / _objc_msgForward_stret ,同時(shí)生成一個(gè)新的 aliasSelector 指向原來(lái)的 IMP,并且 hookforwardInvocation函數(shù),通過(guò)forwardInvocation調(diào)用到原來(lái)的IMP。

核心原理:按照上面的思路,當(dāng)被 hookselector 被執(zhí)行的時(shí)候,首先根據(jù) selector找到了 objc_msgForward / _objc_msgForward_stret ,而這個(gè)會(huì)觸發(fā)消息轉(zhuǎn)發(fā),從而進(jìn)入 forwardInvocation。同時(shí)由于forwardInvocation 的指向也被修改了,因此會(huì)轉(zhuǎn)入新的 forwardInvocation函數(shù),在里面執(zhí)行需要嵌入的附加代碼,完成之后,再轉(zhuǎn)回原來(lái)的 IMP。

大致流程如下:

摘至
摘至

-forwardInvocation:方法的實(shí)現(xiàn)給替換掉了,如果程序里真有用到這個(gè)方法對(duì)消息進(jìn)行轉(zhuǎn)發(fā),原來(lái)的邏輯怎么辦?首先我們?cè)谔鎿Q -forwardInvocation:方法前會(huì)新建一個(gè)方法 -ORIGforwardInvocation:,保存原來(lái)的實(shí)現(xiàn)IMP,在新的 -forwardInvocation:實(shí)現(xiàn)里做了個(gè)判斷,如果轉(zhuǎn)發(fā)的方法是我們想改寫的,就走我們的邏輯,若不是,就調(diào) -ORIGforwardInvocation:走原來(lái)的流程。

將了這么多可能有些饒。Talk is sheap,show me the code

源碼分析

從頭文件中可以看到使用aspects有兩種使用方式:

  • 1.類方法
  • 2.實(shí)例方法
/// 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;

兩者的主要原理基本差不多.

先來(lái)看看有哪些定義:

AspectOptions

typedef NS_OPTIONS(NSUInteger, AspectOptions) {
    AspectPositionAfter   = 0,            /// Called after the original implementation (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.
};

定義切片的調(diào)用時(shí)機(jī)

AspectErrorCode

typedef NS_ENUM(NSUInteger, AspectErrorCode) {
    AspectErrorSelectorBlacklisted,                   /// Selectors like release, retain, 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.
    AspectErrorIncompatibleBlockSignature,            /// The block signature does not match the method or is too large.

    AspectErrorRemoveObjectAlreadyDeallocated = 100   /// (for removing) The object hooked is already deallocated.
};

這里定義了在執(zhí)行的時(shí)候的錯(cuò)誤碼,在平時(shí)開(kāi)發(fā)中我們也經(jīng)常使用這種方式,尤其是在定義網(wǎng)絡(luò)請(qǐng)求的時(shí)候。

AspectsContainer

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

一個(gè)對(duì)象或者類的所有的 Aspects 整體情況,注意這里數(shù)組是通過(guò)atomic修飾的。
關(guān)于atomic需要注意在默認(rèn)情況下,由編譯器所合成的方法會(huì)通過(guò)鎖定機(jī)制確保其原子性(atomicity)。如果屬性具備nonatomic特質(zhì),則不需要同步鎖。

注意一共有兩中容器,一個(gè)是對(duì)象的切片,一個(gè)是類的切片。

AspectIdentifier

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

一個(gè)Aspect的具體內(nèi)容。主要包含了單個(gè)的 aspect 的具體信息,包括執(zhí)行時(shí)機(jī),要執(zhí)行 block 所需要用到的具體信息:包括方法簽名、參數(shù)等等。其實(shí)就是將我們傳入的bloc,包裝成AspectIdentifier,便于后續(xù)使用。通過(guò)我們替換的block實(shí)例化。也就是將我們傳入的block,包裝成了AspectIdentifier

AspectInfo

@interface AspectInfo : NSObject <AspctInfo>
- (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

主要是 NSInvocation 信息。將NSInvocation包裝一層,比如參數(shù)信息等。便于直接使用。

AspectTracker

@interface AspectTracker : NSObject
- (id)initWithTrackedClass:(Class)trackedClass parent:(AspectTracker *)parent;
@property (nonatomic, strong) Class trackedClass;
@property (nonatomic, strong) NSMutableSet *selectorNames;
@property (nonatomic, weak) AspectTracker *parentEntry;
@end

用于跟蹤所改變的類,打上標(biāo)記,用于替換類方法,防止重復(fù)替換類方法

流程


讀源碼是一件辛苦的事情。????

__Block的使用

做過(guò)iOS的都知道,__BlockARCXMRC環(huán)境修飾對(duì)象下是不同的。具體的內(nèi)容可以看看我的另一篇文章。這里只給出結(jié)論:

ARC環(huán)境下,__Block會(huì)對(duì)修飾的對(duì)象強(qiáng)引用,在MRC環(huán)境下對(duì)修飾的對(duì)象不會(huì)強(qiáng)引用。而且__block修飾局部變量,表示這個(gè)對(duì)象是可以在block內(nèi)部修改,如果不這樣寫,會(huì)報(bào)Variable is not assignable (missing__block type specifier)的錯(cuò)誤。

來(lái)看看代碼:

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 *aspectContainer = aspect_getContainerForObject(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;
}

注意這里的__block AspectIdentifier *identifier = nil;為什么要這樣寫呢。上面已經(jīng)說(shuō)過(guò)得很清楚。因?yàn)?code>identifier是局部變量,如果不加__block修飾,block里面不能改變identifier。

自旋鎖(OSSpinLockLock)

Aspect是線程安全的,那么它是通過(guò)什么方式辦到的呢。如果你對(duì)iOS中的幾個(gè)鎖不清楚,可以看看我的另一篇文章,里面有介紹。

自旋鎖是效率比較高的一種鎖,相比@synchronized來(lái)說(shuō)效率高得多。但是需要注意,如果訪問(wèn)這個(gè)所的線程不是同一優(yōu)先級(jí)的話,會(huì)有死鎖的潛在風(fēng)險(xiǎn)。具體原因請(qǐng)看YYKit作者博客。

static void aspect_performLocked(dispatch_block_t block) {
    static OSSpinLock aspect_lock = OS_SPINLOCK_INIT;
    OSSpinLockLock(&aspect_lock);
    // 加鎖執(zhí)行block
    block();
    // 執(zhí)行完之后釋放鎖
    OSSpinLockUnlock(&aspect_lock);
}

通過(guò)這樣的加鎖方式,所以Aspect作者說(shuō)它是線程安全的。

真正燒腦環(huán)節(jié)

執(zhí)行的入口

 aspect_performLocked(^{
        if (aspect_isSelectorAllowedAndTrack(self, selector, options, error)) {
            AspectsContainer *aspectContainer = aspect_getContainerForObject(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);
            }
        }
    });

上面這句代碼是執(zhí)行Aspect的入口??词呛?jiǎn)單其實(shí)里面有非常復(fù)雜的地方。

看一看block做了哪些事情。

  • 1.對(duì)出入進(jìn)來(lái)的參數(shù)進(jìn)行檢驗(yàn),保證參數(shù)合法
  • 2.創(chuàng)建aspect容器,注意容器是懶加載形式動(dòng)態(tài)添加到NSObject分類中作為屬性。
  • 3.根據(jù)參數(shù),比如selector,option,創(chuàng)建AspectIdentifier實(shí)例,上面已經(jīng)說(shuō)過(guò)AspectIdentifier主要包含了單個(gè)的 Aspect的具體信息,包括執(zhí)行時(shí)機(jī),要執(zhí)行block 所需要用到的具體信息。
  • 4.將單個(gè)的 Aspect 的具體信息加到屬性aspectContainer
  • 5.最為重要的部分進(jìn)行Hook操作,生成子類,類型編碼處理,方法替換等。

下面就對(duì)上面5個(gè)部分分別仔細(xì)的分析。

參數(shù)檢驗(yàn)

嚴(yán)格來(lái)說(shuō),在調(diào)用每一個(gè)方法的時(shí)候都需要對(duì)傳入進(jìn)來(lái)的參數(shù)做一次校驗(yàn)。尤其是在做SDK的時(shí)候,因?yàn)槟愀静恢劳饷鎮(zhèn)鬟M(jìn)來(lái)的是什么數(shù)據(jù),到底是否為空,數(shù)據(jù)類型是否正確。平時(shí)開(kāi)發(fā)的過(guò)程中,由于我們都知道傳入的參數(shù)大部分來(lái)說(shuō)都是我們復(fù)合預(yù)期的所以就沒(méi)有做什么檢驗(yàn)工作。

回到Aspect中。檢驗(yàn)的內(nèi)容主要有如下幾個(gè):

  • 1.Swizzle了不能Swizzle的方法,比如@retain", @"release", @"autorelease", @"forwardInvocation:":如果替換了這樣的方法,我們是不能成功進(jìn)行Swizzle的。
  • 2.傳入的執(zhí)行時(shí)機(jī)是否正確,比如對(duì)于dealloc方法,`Swizzle之能在之前進(jìn)行調(diào)用。
  • 3.對(duì)象或者類是否響應(yīng)傳入的selector
  • 4.如果替換的是類方法,則進(jìn)行是否重復(fù)替換的檢查

這里重點(diǎn)捋一捋,類方法的檢驗(yàn)參數(shù),為了檢驗(yàn)類的方法只能修改一次。

 if (class_isMetaClass(object_getClass(self))) {
        Class klass = [self class];
        NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict();
        Class currentClass = [self class];
        do {
            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!
                    return YES;
                }
            }
        }while ((currentClass = class_getSuperclass(currentClass)));

        // Add the selector as being modified.
        currentClass = klass;
        AspectTracker *parentTracker = nil;
        do {
            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)));
    }

  • 如何判斷傳入的是類而不是對(duì)象:class_isMetaClass(object_getClass(self))),object_getClass是獲取當(dāng)前對(duì)象由什么實(shí)例化。

類方法只能替換一次,是在整個(gè)類的繼承樹(shù)上校驗(yàn),而不只是單單的一個(gè)類,從下面代碼可以看出:

 do {
            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!
                    return YES;
                }
            }
        }while ((currentClass = class_getSuperclass(currentClass)));


注意這句(currentClass = class_getSuperclass(currentClass)當(dāng)且只有當(dāng)這個(gè)類為根類的時(shí)候才不會(huì)繼續(xù)循環(huán)查找。

再來(lái)看看這段:

currentClass = klass;
        AspectTracker *parentTracker = nil;
        do {
            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)));

這段的作用就是如果類被修改了,給其父類打上標(biāo)記。然后結(jié)合上面的判斷是否重復(fù)替換。這里為什么要用父類呢。這個(gè)runtime類與父類,根類關(guān)系有關(guān)為了有效的遍歷,需要找到一個(gè)退出的條件,而退出的條件,結(jié)合到runtime就是根類沒(méi)有父類。這就是退出的條件。

如果還沒(méi)有弄懂,回去看看runtime的基本知識(shí)

創(chuàng)建Aspect容器

AspectsContainer的作用是為了保存整個(gè)Apects的情況,包括添加/刪除的aspect,根據(jù)執(zhí)行的時(shí)機(jī)(before,instead,after)而保存的所有的aspcet。其中用到了比較常用的動(dòng)態(tài)添加屬性。

static AspectsContainer *aspect_getContainerForObject(NSObject *self, SEL selector) {
    NSCParameterAssert(self);
    // 得到新的SEL(加上了前綴aspects_)并新增為屬性
    SEL aliasSelector = aspect_aliasForSelector(selector);

    // 得到和aliasSelector相對(duì)應(yīng)的AspectsContainer
    AspectsContainer *aspectContainer = objc_getAssociatedObject(self, aliasSelector);
    if (!aspectContainer) {
        aspectContainer = [AspectsContainer new];
        objc_setAssociatedObject(self, aliasSelector, aspectContainer, OBJC_ASSOCIATION_RETAIN);
    }
    return aspectContainer;
}

這一步還是比較簡(jiǎn)單。

單個(gè)的 Aspect 的具體信息AspectIdentifier

上面介紹的AspectsContainer里面裝的具體的東東就是AspectIdentifier。AspectIdentifier包含的是具體到每一個(gè)aspect的具體信息,直接看屬性:

@property (nonatomic, assign) SEL selector;
@property (nonatomic, strong) id block;
@property (nonatomic, strong) NSMethodSignature *blockSignature;
@property (nonatomic, weak) id object; // 具體信息所屬類,用weak
@property (nonatomic, assign) AspectOptions options;

初始化方法將需要的比如:sel、block、option,傳進(jìn)去。`

  • (instancetype)identifierWithSelector:(SEL)selector object:(id)object options:(AspectOptions)options block:(id)block error:(NSError **)error`

需要重點(diǎn)注意方法簽名NSMethodSignature,因?yàn)槲覀冊(cè)谑褂?code>Aspect的時(shí)候是直接通過(guò)block來(lái)替換方法的,所以需要將我們傳入的block轉(zhuǎn)換為具體的方法。這也是為什么會(huì)在AspectIdentifier中多一個(gè)方法簽名的屬性。

在介紹方法簽名之前需要對(duì)block具體的結(jié)構(gòu)有一定了解。在Aspect中,我們定義了一個(gè)結(jié)構(gòu)體用來(lái)代替系統(tǒng)的block??傮w結(jié)構(gòu)其實(shí)和系統(tǒng)的一樣。

// 模仿系統(tǒng)的block結(jié)構(gòu)
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;

看一看將block轉(zhuǎn)為方法簽名的代碼:

static NSMethodSignature *aspect_blockMethodSignature(id block, NSError **error) {
    // 將block轉(zhuǎn)換為自定義的形式
    AspectBlockRef layout = (__bridge void *)block;
    // 過(guò)濾
    if (!(layout->flags & AspectBlockFlagsHasSignature)) {// 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.", block];
        AspectError(AspectErrorMissingBlockSignature, description);
        return nil;
    }
    const char *signature = (*(const char **)desc);
    // Returns an NSMethodSignature object for the given Objective-C method type string.
    // 根據(jù)類型編碼返回真正方法簽名
    return [NSMethodSignature signatureWithObjCTypes:signature];
}

block是作為id類型進(jìn)行傳遞的,而且是全局類型的block。如果轉(zhuǎn)換成功,layout里面都會(huì)有相應(yīng)的值。

這句話const char *signature = (*(const char **)desc);得到我們傳入block的類型編碼。通過(guò)類型編碼得到block所對(duì)應(yīng)的方法簽名。

這里大致給出返回值為空,無(wú)參數(shù)的block轉(zhuǎn)換之后的方法簽名的結(jié)構(gòu):

<NSMethodSignature: 0x7f9219d064e0>
    number of arguments = 1
    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 = 0, offset adjust = 0, size = 8, size adjust = 0}
        memory {offset = 0, size = 8}

得到了方法簽名,需要對(duì)這個(gè)方法簽名和替換實(shí)際方法進(jìn)行比較。從這里可以感受到做一個(gè)第三方對(duì)參數(shù)的檢測(cè)是非常非常重要的。這也是我們平時(shí)在應(yīng)用開(kāi)發(fā)中所欠缺的。

那怎么和原來(lái)的方法比較呢。比較直接的方法就是,拿到替換方法的方法簽名和我們將block轉(zhuǎn)換之后的方法簽名對(duì)比。

直接看到代碼:

// 原方法簽名
    NSMethodSignature *methodSignature = [[object class] instanceMethodSignatureForSelector:selector];

    // 參數(shù)不匹配
    if (blockSignature.numberOfArguments > methodSignature.numberOfArguments) {
        signaturesMatch = NO;
    }else {
        if (blockSignature.numberOfArguments > 1) {
            // blockSignature參數(shù)沒(méi)有_cmd,
            const char *blockType = [blockSignature getArgumentTypeAtIndex:1];
            // 類型編碼
            if (blockType[0] != '@') {
                signaturesMatch = NO;
            }
        }

        // Argument 0 is self/block, argument 1 is SEL or id<AspectInfo>. We start comparing at argument 2.
        // 對(duì)于block來(lái)說(shuō)
        // The block can have less 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.
                // 參數(shù)匹配
                if (!methodType || !blockType || methodType[0] != blockType[0]) {
                    signaturesMatch = NO;
                    break;
                }
            }
        }
    }

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

說(shuō)一說(shuō)為什么要從第二個(gè)參數(shù)開(kāi)始比較呢。首先我們常見(jiàn)的方法都有連個(gè)隱藏的參數(shù),一個(gè)是__cmd,一個(gè)是self。而方法簽名的參數(shù)也有兩個(gè)(針對(duì)一個(gè)返回值為空,參數(shù)為空的方法)。第一個(gè)是self,第二個(gè)是SEL。源代碼有相關(guān)說(shuō)明。

我直接打印一下:

  • methodSignature:

  • <NSMethodSignature: 0x7f9219d0cab0>
    number of arguments = 2
    frame size = 224
    is special struct return? NO
    return value: -------- -------- -------- --------
    type encoding (B) 'B'
    flags {}
    modifiers {}
    frame {offset = 0, offset adjust = 0, size = 8, size adjust = -7}
    memory {offset = 0, size = 1}
    argument 0: -------- -------- -------- --------
    type encoding (@) '@'
    flags {isObject}
    modifiers {}
    frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0}
    memory {offset = 0, size = 8}
    argument 1: -------- -------- -------- --------
    type encoding (:) ':'
    flags {}
    modifiers {}
    frame {offset = 8, offset adjust = 0, size = 8, size adjust = 0}
    memory {offset = 0, size = 8}

  • 
    
  • blockSignature

<NSMethodSignature: 0x7f9219d064e0>
number of arguments = 1
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 = 0, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
argument 1: -------- -------- -------- --------
type encoding (:) ':'
flags {}
modifiers {}
frame {offset = 8, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}


將`block`轉(zhuǎn)換為方法簽名之后就可以給`AspectIdentifier`具體屬性賦值了。

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


### 將具體的`AspectIdentifier `添加到容器中

這一步比較簡(jiǎn)單`[aspectContainer addAspect:identifier withOptions:options];`

### 方法替換

方法替換是這個(gè)庫(kù)最為核心的部分,也是最難的部分。里面的思路以及方法很多都值得學(xué)習(xí)。這部分也是自己花了很多時(shí)間去了解。

大致的步驟:

- 1.創(chuàng)建子類,`hook`子類
- 2.處理實(shí)現(xiàn)過(guò)`MsgForwardIMP`的類或者對(duì)象
- 3.將需要替換的`selector`,指向`_objc_msgForward`

來(lái)具體看一看

#### 創(chuàng)建子類

創(chuàng)建子類將`runtime`用到了極致。其實(shí)我們所做的替換完全是在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建子類的時(shí)候?qū)崿F(xiàn)的。這樣對(duì)原來(lái)替換的類或者對(duì)象沒(méi)有任何影響而且可以在子類基礎(chǔ)上新增或者刪除`aspect`。

注意`Class statedClass = self.class;`和`Class baseClass = object_getClass(self);`的區(qū)別,前者獲取類對(duì)象,后者獲取本類是由什么實(shí)例化。

**所有的操作是在類對(duì)象基礎(chǔ)上操作的,而不是一個(gè)對(duì)象**最為重要的就是`aspect_swizzleClassInPlace`方法

static Class aspect_swizzleClassInPlace(Class klass) {
NSCParameterAssert(klass);
NSString *className = NSStringFromClass(klass);

// 1.保證線程安全,2保證數(shù)組單例
_aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) {
    // 不包含這個(gè)類,如果已經(jīng)包含
    if (![swizzledClasses containsObject:className]) {
        /** 深藏的核心部分 **/
        // 將原IMP指向forwardInvocation
        aspect_swizzleForwardInvocation(klass);

        // 添加進(jìn)來(lái)
        [swizzledClasses addObject:className];
    }
});
return klass;

}

為了保證線程安全,所以用了`_aspect_modifySwizzledClasses`是單例方法。將傳入的`block`安全執(zhí)行

static void _aspect_modifySwizzledClasses(void (^block)(NSMutableSet *swizzledClasses)) {
static NSMutableSet *swizzledClasses;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
swizzledClasses = [NSMutableSet new];
});
@synchronized(swizzledClasses) {
block(swizzledClasses);
}
}


來(lái)看第一個(gè)重頭戲:**aspect_swizzleForwardInvocation**這個(gè)方法的目的就是將本類中的`forwardInvocation`方法替換為我們自定義的`__ASPECTS_ARE_BEING_CALLED__`方法。這樣只要類沒(méi)有找到消息處理著都會(huì)走到自定義的`__ASPECTS_ARE_BEING_CALLED__ `。這樣就可以統(tǒng)一處理了。

這里有個(gè)小小的問(wèn)題,如果`forwardInvocation `已經(jīng)被替換了,那么就需要特殊處理。比如這里判斷的方法是,如果原來(lái)實(shí)現(xiàn)過(guò)`forwardInvocation `。則新增一個(gè)方法`AspectsForwardInvocationSelectorName`,指向`originalImplementation `。這個(gè)時(shí)候已經(jīng)`forwardInvocation`已經(jīng)指向了我們自定義的`AspectsForwardInvocationSelectorName `。所以是同理的。

IMP originalImplementation = class_replaceMethod(klass, @selector(forwardInvocation:), (IMP)ASPECTS_ARE_BEING_CALLED, "v@:@");

if (originalImplementation) {
    // 將__aspects_forwardInvocation:指向originalImplementation,
    // 將originalImplementation添加到Method,以便下次調(diào)用,直接就可以了
    class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName), originalImplementation, "v@:@");
}

再來(lái)看看`__ASPECTS_ARE_BEING_CALLED__`這個(gè)方法如何將轉(zhuǎn)發(fā)過(guò)來(lái)的參數(shù)成功的轉(zhuǎn)換為我們需要的參數(shù)。

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;
// 本對(duì)象的AspectsContainer,添加到對(duì)象的aspect
AspectsContainer *objectContainer = objc_getAssociatedObject(self, aliasSelector);
// 這個(gè)類的AspectsContainer,添加類上面的aspect
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 {
    Class klass = object_getClass(invocation.target);
    do {
        if ((respondsToAlias = [klass instancesRespondToSelector:aliasSelector])) {
            // 直接調(diào)用
            [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)];

}


用自定的`aliasSelector`替換掉`invocation`的`selector`。然后將`invocation`轉(zhuǎn)化為`AspectInfo `。`AspectInfo `主要包含了`invocation `相關(guān)的信息,比如參數(shù)數(shù)組。如何獲取參數(shù)數(shù)組呢?代碼是通過(guò)為`NSInvocation`寫一個(gè)分類來(lái)實(shí)現(xiàn)。原理就是涉及到類型編碼的那塊。來(lái)看看:

// Thanks to the ReactiveCocoa team for providing a generic solution for this.

  • (id)aspect_argumentAtIndex:(NSUInteger)index {
    const char *argType = [self.methodSignature getArgumentTypeAtIndex:index];
    // 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)) == 0 || strcmp(argType, @encode(Class)) == 0) {
    __autoreleasing id returnObj;
    [self getArgument:&returnObj atIndex:(NSInteger)index];
    return returnObj;
} else if (strcmp(argType, @encode(SEL)) == 0) {
    SEL selector = 0;
    [self getArgument:&selector atIndex:(NSInteger)index];
    return 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.
} else if (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(short);
} else 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(argType, @encode(unsigned char)) == 0) {
    WRAP_AND_RETURN(unsigned 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 if (strcmp(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 if (strcmp(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

}

有幾個(gè)基礎(chǔ)的地方。

- @encode:獲取類型編碼:比如`strcmp(argType, @encode(SEL)) == 0`就是判斷參數(shù)`argType`是否為SEL類型。
- WRAP_AND_RETURN:是一個(gè)宏定義,為了將一般類型,比如`Bool`,`Int`轉(zhuǎn)為對(duì)象,然后能夠添加到數(shù)組中。

開(kāi)始調(diào)用通過(guò)宏定義`aspect_invoke`

define aspect_invoke(aspects, info) \

for (AspectIdentifier *aspect in aspects) {
[aspect invokeWithInfo:info];
if (aspect.options & AspectOptionAutomaticRemoval) {
aspectsToRemove = [aspectsToRemove?:@[] arrayByAddingObject:aspect];
}
}

為什么這里用宏定義來(lái)調(diào)用呢。因?yàn)楹昴軌蜃屛耀@得堆棧信息。

**進(jìn)入到了最后調(diào)用的地方了。**

  • (BOOL)invokeWithInfo:(id<AspectInfo>)info {
    NSInvocation *blockInvocation = [NSInvocation invocationWithMethodSignature:self.blockSignature];
    NSInvocation *originalInvocation = info.originalInvocation;
    NSUInteger 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.
    /**

    • index: Indices 0 and 1 indicate the hidden arguments self and _cmd, respectively; you should set these values directly with the target and selector properties
      */
      if (numberOfArguments > 1) {
      [blockInvocation setArgument:&info atIndex:1];
      }

    void *argBuf = NULL;
    // 根據(jù)NSInvocation參數(shù)規(guī)則,從第二個(gè)參數(shù)開(kāi)始取
    // 參數(shù)處理
    for (NSUInteger idx = 2; idx < numberOfArguments; idx++) {
    // 取出類型編碼
    const char *type = [originalInvocation.methodSignature getArgumentTypeAtIndex:idx];
    NSUInteger argSize;
    NSGetSizeAndAlignment(type, &argSize, NULL);

      if (!(argBuf = reallocf(argBuf, argSize))) {
          AspectLogError(@"Failed to allocate memory for block invocation.");
          return NO;
      }
    
      // 從originalInvocation取出參數(shù)
      [originalInvocation getArgument:argBuf atIndex:idx];
      // 給blockInvocation設(shè)置參數(shù)
      [blockInvocation setArgument:argBuf atIndex:idx];
    

    }

    // 調(diào)用
    [blockInvocation invokeWithTarget:self.block];

    if (argBuf != NULL) {
    free(argBuf);
    }
    return YES;
    }


用之前將`block`轉(zhuǎn)為的`blockSignature`初始化`blockSignature`得到`invocation`。然后處理參數(shù),如果參數(shù)`block`中的參數(shù)大于1個(gè),則把包裝成`AspectInfo `。

然后從`originalInvocation `中取出參數(shù)給`blockInvocation `賦值。最后調(diào)用。` [blockInvocation invokeWithTarget:self.block];`這里`Target`設(shè)置為`self.block`。注意`You must set the receiver’s selector and argument values before calling this method`

#### 在子類中將替換的`selector`指向`_objc_msgForward`

Method targetMethod = class_getInstanceMethod(kclass, selector);
IMP targetMethodIMP = method_getImplementation(targetMethod);

由于`kclass`是替換類的子類,所以正常情況下`targetMethod `不會(huì)為空。相當(dāng)雨得到原有的`IMP`,如果原有的`IMP`就是指向的`_objc_msgForward`。則不用處理了。我們已經(jīng)在前面處理過(guò)了。

直接來(lái)看正常流程

if (!aspect_isMsgForwardIMP(targetMethodIMP)) {
// Make a method alias for the existing method implementation, it not already copied.
const char *typeEncoding = method_getTypeEncoding(targetMethod);

    // 3- 得到替換的SEL
    SEL aliasSelector = aspect_aliasForSelector(selector);
    // 子類沒(méi)有這個(gè)方法,添加方法到子類
    if (![klass instancesRespondToSelector:aliasSelector]) {
        __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);
    }

    // 使用forwardInvocation來(lái)統(tǒng)一處理,將原SEL指向原SEL找不到方法的forwardInvocation的IMP
    // We use forwardInvocation to hook in.

    // 4- 將需要替換的selector,指向_objc_msgForward,進(jìn)行統(tǒng)一處理
    class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);
    AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
}
先獲取`targetMethodIMP `的類型編碼。然后將我們自定義的`aliasSelector `添加的子類上。最后進(jìn)行替換。`class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);`最后來(lái)看看這個(gè)函數(shù):

static IMP aspect_getMsgForwardIMP(NSObject *self, SEL selector) {
// self 成為子類
IMP msgForwardIMP = _objc_msgForward;

// 需要兼容__arm64__,用不同的方式

if !defined(arm64)

// As an ugly internal runtime implementation detail in the 32bit runtime, we need to determine of the method we hook returns a struct or anything larger than id.
// https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/000-Introduction/introduction.html
// https://github.com/ReactiveCocoa/ReactiveCocoa/issues/783
// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf (Section 5.4)
Method method = class_getInstanceMethod(self.class, selector);
// 類型編碼字符
const char *encoding = method_getTypeEncoding(method);
BOOL methodReturnsStructValue = encoding[0] == _C_STRUCT_B;
if (methodReturnsStructValue) {
    // 通過(guò)Try Cathch 捕獲異常
    @try {
        NSUInteger valueSize = 0;
        NSGetSizeAndAlignment(encoding, &valueSize, NULL);

        if (valueSize == 1 || valueSize == 2 || valueSize == 4 || valueSize == 8) {
            methodReturnsStructValue = NO;
        }
    } @catch (NSException *e) {}
}
if (methodReturnsStructValue) {
    msgForwardIMP = (IMP)_objc_msgForward_stret;
}

endif

return msgForwardIMP;

}


這個(gè)函數(shù)主要是處理`__arm64__ `中`var_list`結(jié)構(gòu)變了。[可參考](https://blog.nelhage.com/2010/10/amd64-and-va_arg)

` BOOL methodReturnsStructValue = encoding[0] == _C_STRUCT_B;`這句話是判斷類型編碼第一個(gè)字符是否為`_C_STRUCT_B `

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'

這個(gè)是對(duì)類型編碼的宏定義。太深了搞起來(lái)太費(fèi)腦子了。大概了解到這個(gè)程度吧。

**替換之后,調(diào)用原有的Method的時(shí)候,就會(huì)消息轉(zhuǎn)發(fā)**

## 寫在最后

> 平時(shí)接觸事物決定了我們的高度。如果整天圍著`UIKit`轉(zhuǎn),技術(shù)也就那樣。居安思危!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容