Objective-C 方法交換實踐(三) - Aspects 源碼解析

1、類與變量

AspectOptions
typedef NS_OPTIONS(NSUInteger, AspectOptions) {

    AspectPositionAfter   = 0,            /// 原方法以後 (default)

    AspectPositionInstead = 1,            /// 替換原方法

    AspectPositionBefore  = 2,            /// 原方法以前

    AspectOptionAutomaticRemoval = 1 << 3 /// 執行一次後就移除

};
AspectsContainer

存儲AspectIdentifier,總共有三個數組,分別存儲 上面的 After Instead Before 的各類 Aspects。數組

AspectIdentifier
+ (instancetype)identifierWithSelector:(SEL)selector object:(id)object options:(AspectOptions)options block:(id)block error:(NSError **)error;

從建立方法就能夠看出,他是用來記錄 要替換的對象object,替換的原方法selector,替換的類型options,以及執行的代碼block。其中block的參數是 id<AspectInfo>類型的。ide

AspectInfo

一種是協議名,標記當前 NSinvocation的一些環境atom

@protocol AspectInfo <NSObject>

/// The instance that is currently hooked.
- (id)instance;

/// The original invocation of the hooked method.
- (NSInvocation *)originalInvocation;

/// All method arguments, boxed. This is lazily evaluated.
- (NSArray *)arguments;

@end

一種是類名,基本上就是實現上面的協議的類。lua

@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

2、具體過程

1.aspect_isSelectorAllowedAndTrack 檢測是否可以swizzle

檢測不能是如下方法 @"retain", @"release", @"autorelease", @"forwardInvocation:"code

對於"dealloc"方法位置只能是 AspectPositionBefore對象

被交換的方法是否未實現get

若是是metaClass,若是子類已經hook,返回;若是父類已經hook,返回。否者標記hook的 SEL,爲父類標記已經hook 的 Child Class。it

2.aspect_getContainerForObject

獲得當前 select 對應的 AspectsContainer, 不存在就建立,經過關聯對象的方式存儲在類中io

3.AspectIdentifier

建立 AspectIdentifier而且添加到AspectsContainer裏面去class

4.aspect_prepareClassAndHookSelector 進行swizzle操做
  • 若是已經操做過,返回

  • 若是是metaClass,對metaClass 進行 A操做
  • 若是實例對象進行過 object_setClass 操做,對 class 進行A操做
  • 不然建立一個 subclass ,對其進行 object_setClass 操做,而後對這個 subclass 進行 A 操做,以後把 當前對象的 isa 指向 subclass

A操做是:

替換類的 forwardInvocation 方法爲 __ASPECTS_ARE_BEING_CALLED__

爲類增長 aliasSelector 指向原 selector

把原 selector 指向 _objc_msgForward或者_objc_msgForward_stret

固然裏面還有一些容錯判斷

5.__ASPECTS_ARE_BEING_CALLED__ 流程

取出當前對象(object)的 AspectsContainer 和 類(class)的 AspectsContainer ,調用 Before hooks 的一些方法,調用 instead 的方法,調用 after 的方法

對沒有instead的狀況檢測是否有原方法,沒有就走原 forwardInvocation方法或者走 doesNotRecognizeSelector方法

相關文章
相關標籤/搜索