序言
AOP (Aspect-oriented programming) 譯為 “面向切面編程”,通過預(yù)編譯方式和運(yùn)行期動態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。利用 AOP 可以對業(yè)務(wù)邏輯的各個部分進(jìn)行隔離剃法,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發(fā)的效率玩荠。
AOP 目前是較為熱門的一個話題衔瓮,盡管你也許沒有聽說過它浊猾,但是你的項(xiàng)目中可能已經(jīng)滲入了它,例如:用戶統(tǒng)計(jì)(不添加一行代碼即實(shí)現(xiàn)對所有 ViewController 的跟蹤日志)热鞍。
Aspects 作為 Objective-C 語言編寫的 AOP 庫葫慎,適用于 iOS 和 Mac OS X,使用體驗(yàn)簡單愉快薇宠,已經(jīng)在 GitHub 摘得 7k+ Star偷办。Aspects 內(nèi)部實(shí)現(xiàn)比較健全,考慮到了 Hook 安全方面可能發(fā)生的種種問題澄港,非常值得我們學(xué)習(xí)椒涯。
Note: 本文內(nèi)引用 Aspects 源碼版本為 v1.4.1,要求讀者具備一定的 Runtime 知識回梧。
索引
- AOP 簡介
- Aspects 簡介
- Aspects 結(jié)構(gòu)剖析
- Aspects 核心代碼剖析
- 優(yōu)秀 AOP 庫應(yīng)該具備的特質(zhì)
- 總結(jié)
一 AOP簡介
在運(yùn)行時废岂,動態(tài)地將代碼切入到類的指定方法、指定位置上的編程思想就是面向切面的編程漂辐。
AOP (Aspect-oriented programming) 泪喊,即 “面向切面編程” 是一種編程范式,或者說是一種編程思想髓涯,它解決了 OOP (Object-oriented programming) 的延伸問題袒啼。
什么時候需要使用 AOP
一般做用戶頁面統(tǒng)計(jì)時需要
思路: Hook UIViewController 的 viewWillAppear: 和 viewWillDisappear: 方法,在原方法執(zhí)行之后記錄需要統(tǒng)計(jì)的信息上報(bào)即可纬纪。
Note: 簡單通過 Method Swizzling 來 Hook 不是不可以蚓再,但是有很多安全隱患!
二 Aspects簡介
Aspects 是一個使用起來簡單愉快的 AOP 庫包各,使用 Objective-C 編寫摘仅,適用于 iOS 與 Mac OS X。
Aspects 內(nèi)部實(shí)現(xiàn)考慮到了很多 Hook 可能引發(fā)的問題问畅,筆者在看源碼的過程中摳的比較細(xì)娃属,真的是受益匪淺。
Aspects 簡單易用护姆,作者通過在 NSObject (Aspects) 分類中暴露出的兩個接口分別提供了對實(shí)例和 Class 的 Hook 實(shí)現(xiàn):
@interface NSObject (Aspects)
+ (id)aspect_hookSelector:(SEL)selector
withOptions:(AspectOptions)options
usingBlock:(id)block
error:(NSError **)error;
- (id)aspect_hookSelector:(SEL)selector
withOptions:(AspectOptions)options
usingBlock:(id)block
error:(NSError **)error;
@end
Aspects 支持實(shí)例 Hook矾端,相較其他 Objective-C AOP 庫而言可操作粒度更小,適合的場景更加多樣化卵皂。作為使用者無需進(jìn)行更多的操作即可 Hook 指定實(shí)例或者 Class 的指定 SEL秩铆,AspectOptions 參數(shù)可以指定 Hook 的點(diǎn),以及是否執(zhí)行一次之后就撤銷 Hook。
2.1 Aspects 結(jié)構(gòu)剖析
盡管 Aspects 只有不到千行的源碼殴玛,但是其內(nèi)部實(shí)現(xiàn)考慮到了很多 Hook 相關(guān)的安全問題和其他細(xì)節(jié)捅膘,對比其他 Objective-C AOP 開源項(xiàng)目來說 Aspects 更為健全,所以我自己在扒 Aspects 源碼時也看的比較仔細(xì)滚粟。
2.2 Aspects 內(nèi)部結(jié)構(gòu)
- Aspects 內(nèi)部定義了兩個協(xié)議:
AspectToken
:用于注銷 Hook
AspectInfo
: 嵌入 Hook 中的 Block 首位參數(shù)
- Aspects 內(nèi)部還定義了 4 個類:
AspectInfo
:切面信息寻仗,遵循 AspectInfo 協(xié)議
AspectIdentifier
:切面 ID,應(yīng)該遵循 AspectToken 協(xié)議(作者漏掉了凡壤,已提 PR)
AspectsContainer
:切面容器
AspectTracker
:切面跟蹤器
- 一個結(jié)構(gòu)體
AspectBlockRef
:即 _AspectBlock愧沟,充當(dāng)內(nèi)部 Block
4.兩個內(nèi)部靜態(tài)全局變量:
static NSMutableDictionary *swizzledClassesDict;
static NSMutableSet *swizzledClasses;
2.3 各個知識點(diǎn)詳解
2.3.1 AspectToken
AspectToken 協(xié)議旨在讓使用者可以靈活的注銷之前添加過的 Hook,內(nèi)部規(guī)定遵守此協(xié)議的對象須實(shí)現(xiàn) remove 方法鲤遥。
/// 不透明的 Aspect Token,用于注銷 Hook
@protocol AspectToken /// 注銷一個 aspect.
/// 返回 YES 表示注銷成功林艘,否則返回 NO
- (BOOL)remove;
@end
2.3.2 AspectInfo
AspectInfo 協(xié)議旨在規(guī)范對一個切面盖奈,即 aspect 的 Hook 內(nèi)部信息的紕漏,我們在 Hook 時添加切面的 Block 第一個參數(shù)就遵守此協(xié)議狐援。
/// AspectInfo 協(xié)議是我們塊語法的第一個參數(shù)钢坦。
@protocol AspectInfo /// 當(dāng)前被 Hook 的實(shí)例
- (id)instance;
/// 被 Hook 方法的原始 invocation
- (NSInvocation *)originalInvocation;
/// 所有方法參數(shù)(裝箱之后的)惰性執(zhí)行
- (NSArray *)arguments;
@end
Note: 裝箱是一個開銷昂貴操作,所以用到再去執(zhí)行啥酱。
2.3.3 AspectInfo
Note: AspectInfo 在這里是一個 Class爹凹,其遵守上文中講到的 AspectInfo 協(xié)議,不要混淆镶殷。
AspectInfo 類定義:
@interface AspectInfo : NSObject - (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
Note: 關(guān)于裝箱禾酱,對于提供一個 NSInvocation 就可以拿到其 arguments 這一點(diǎn)上,ReactiveCocoa 團(tuán)隊(duì)提供了很大貢獻(xiàn)(細(xì)節(jié)見 Aspects 內(nèi)部 NSInvocation 分類)绘趋。
AspectInfo 比較簡單颤陶,參考 ReactiveCocoa 團(tuán)隊(duì)提供的 NSInvocation 參數(shù)通用方法可將參數(shù)裝箱為 NSValue,簡單來說 AspectInfo 扮演了一個提供 Hook 信息的角色陷遮。
2.3.4 AspectIdentifier
AspectIdentifier 類定義:
@interface AspectIdentifier : NSObject
+ (instancetype)identifierWithSelector:(SEL)selector object:(id)object options:(AspectOptions)options block:(id)block error:(NSError **)error;
- (BOOL)invokeWithInfo:(id)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
Note: AspectIdentifier 實(shí)際上是添加切面的 Block 的第一個參數(shù)滓走,其應(yīng)該遵循 AspectToken 協(xié)議,事實(shí)上也的確如此帽馋,其提供了 remove 方法的實(shí)現(xiàn)搅方。
AspectIdentifier 內(nèi)部需要注意的是由于使用 Block 來寫 Hook 中我們加的料,這里生成了 blockSignature绽族,在 AspectIdentifier 初始化的過程中會去判斷 blockSignature 與入?yún)?object 的 selector 得到的 methodSignature 的兼容性姨涡,兼容性判斷成功才會順利初始化。
2.3.5 AspectsContainer
AspectsContainer 類定義:
@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
AspectsContainer 作為切面的容器類项秉,關(guān)聯(lián)指定對象的指定方法绣溜,內(nèi)部有三個切面隊(duì)列,分別容納關(guān)聯(lián)指定對象的指定方法中相對應(yīng) AspectOption 的 Hook:
- NSArray *beforeAspects; - AspectPositionBefore
- NSArray *insteadAspects; - AspectPositionInstead
- NSArray *afterAspects; - AspectPositionAfter
為什么要說關(guān)聯(lián)呢娄蔼?因?yàn)?AspectsContainer 是在 NSObject 分類中通過 AssociatedObject 方法與當(dāng)前要 Hook 的目標(biāo)關(guān)聯(lián)在一起的怖喻。
Note: 關(guān)聯(lián)目標(biāo)是 Hook 之后的 Selector底哗,即 aliasSelector(原始 SEL 名稱加 aspects_ 前綴對應(yīng)的 SEL)。
2.3.6 AspectTracker
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
AspectTracker 作為切面追蹤器锚沸,原理大致如下:
// 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)currentClass] = tracker;
}
[tracker.selectorNames addObject:selectorName];
// All superclasses get marked as having a subclass that is modified.
parentTracker = tracker;
}while ((currentClass = class_getSuperclass(currentClass)));
Note: 全局變量 swizzledClassesDict 中的 value 對應(yīng)著 AspectTracker 指針跋选。
就是說 AspectTracker 是從下而上追蹤,最底層的 parentEntry 為 nil哗蜈,父類的 parentEntry 為子類的 tracker前标。
2.3.7 AspectBlockRef
AspectBlockRef,即 struct _AspectBlock距潘,其定義如下:
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;
Note: __unused 宏定義實(shí)際上是 attribute((unused)) GCC 定語炼列,旨在告訴編譯器“如果我沒有在后面使用到這個變量也別警告我”。
2.3.8 Aspects 靜態(tài)全局變量
2.3.8.1 static NSMutableDictionary *swizzledClassesDict;
static NSMutableDictionary *swizzledClassesDict; 在 Aspects 中扮演著已混寫類字典的角色
Aspects 內(nèi)部提供了專門訪問這個全局字典的方法:
static NSMutableDictionary *aspect_getSwizzledClassesDict() {
static NSMutableDictionary *swizzledClassesDict;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
swizzledClassesDict = [NSMutableDictionary new];
});
return swizzledClassesDict;
}
這個全局變量可以簡單理解為記錄整個 Hook 影響的 Class 包含其 SuperClass 的追蹤記錄的全局字典音比。
2.3.8.2 static NSMutableSet *swizzledClasses;
static NSMutableSet *swizzledClasses; 在 Aspects 中擔(dān)當(dāng)記錄已混寫類的角色
Aspects 內(nèi)部提供一個用于修改這個全局變量內(nèi)容的方法:
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);
}
}
Note: 注意 @synchronized(swizzledClasses)俭尖。
這個全局變量記錄了 forwardInvocation: 被混寫的的類名稱。
Note: 注意在用途上與 static NSMutableDictionary *swizzledClassesDict; 區(qū)分理解洞翩。
三 Aspects 核心代碼剖析
Aspects 的整體實(shí)現(xiàn)代碼不超過一千行稽犁,而且考慮的情況也比較全面,非常值得大家花時間去讀一下骚亿,這里給出部分核心碼的理解已亥。
3.1 Hook Class && Hook Instance
Aspects 不光支持 Hook Class 還支持 Hook Instance,這提供了更小粒度的控制来屠,配合 Hook 的撤銷功能可以更加靈活精準(zhǔn)的做我們想做的事~
Aspects 為了能區(qū)別 Class 和 Instance 的邏輯虑椎,實(shí)現(xiàn)了名為 aspect_hookClass 的方法,我認(rèn)為其中的實(shí)現(xiàn)值得我用一部分篇幅來單獨(dú)講解俱笛,也覺得讀者們有必要花點(diǎn)時間理解這里的實(shí)現(xiàn)邏輯绣檬。
static Class aspect_hookClass(NSObject *self, NSError **error) {
// 斷言 self
NSCParameterAssert(self);
// class
Class statedClass = self.class;
// isa
Class baseClass = object_getClass(self);
NSString *className = NSStringFromClass(baseClass);
// 已經(jīng)子類化過了
if ([className hasSuffix:AspectsSubclassSuffix]) {
return baseClass;
// 我們混寫了一個 class 對象,而非一個單獨(dú)的 object
}else if (class_isMetaClass(baseClass)) {
// baseClass 是元類嫂粟,則 self 是 Class 或 MetaClass娇未,混寫 self
return aspect_swizzleClassInPlace((Class)self);
// 可能是一個 KVO'ed class⌒呛纾混寫就位零抬。也要混寫 meta classes。
}else if (statedClass != baseClass) {
// 當(dāng) .class 和 isa 指向不同的情況宽涌,混寫 baseClass
return aspect_swizzleClassInPlace(baseClass);
}
// 默認(rèn)情況下平夜,動態(tài)創(chuàng)建子類
// 拼接子類后綴 AspectsSubclassSuffix
const char *subclassName = [className stringByAppendingString:AspectsSubclassSuffix].UTF8String;
// 嘗試用拼接后綴的名稱獲取 isa
Class subclass = objc_getClass(subclassName);
// 找不到 isa,代表還沒有動態(tài)創(chuàng)建過這個子類
if (subclass == nil) {
// 創(chuàng)建一個 class pair卸亮,baseClass 作為新類的 superClass忽妒,類名為 subclassName
subclass = objc_allocateClassPair(baseClass, subclassName, 0);
if (subclass == nil) { // 返回 nil,即創(chuàng)建失敗
NSString *errrorDesc = [NSString stringWithFormat:@"objc_allocateClassPair failed to allocate class %s.", subclassName];
AspectError(AspectErrorFailedToAllocateClassPair, errrorDesc);
return nil;
}
// 混寫 forwardInvocation:
aspect_swizzleForwardInvocation(subclass);
// subClass.class = statedClass
aspect_hookedGetClass(subclass, statedClass);
// subClass.isa.class = statedClass
aspect_hookedGetClass(object_getClass(subclass), statedClass);
// 注冊新類
objc_registerClassPair(subclass);
}
// 覆蓋 isa
object_setClass(self, subclass);
return subclass;
}
Note: 其實(shí)這里的難點(diǎn)就在于對 .class 和 object_getClass 的區(qū)分。
- .class 當(dāng) target 是 Instance 則返回 Class段直,當(dāng) target 是 Class 則返回自身
- object_getClass 返回 isa 指針的指向
Note: 動態(tài)創(chuàng)建一個 Class 的完整步驟也是我們應(yīng)該注意的吃溅。
- objc_allocateClassPair
- class_addMethod
- class_addIvar
- objc_registerClassPair
3.2 Hook 的實(shí)現(xiàn)
在上面 aspect_hookClass 方法中,不僅僅是返回一個要 Hook 的 Class鸯檬,期間還做了一些細(xì)節(jié)操作决侈,不論是 Class 還是 Instance,都會調(diào)用 aspect_swizzleForwardInvocation 方法喧务,下面對這個方法剖析
static void aspect_swizzleForwardInvocation(Class klass) {
// 斷言 klass
NSCParameterAssert(klass);
// 如果沒有 method赖歌,replace 實(shí)際上會像是 class_addMethod 一樣
IMP originalImplementation = class_replaceMethod(klass, @selector(forwardInvocation:), (IMP)__ASPECTS_ARE_BEING_CALLED__, "v@:@");
// 拿到 originalImplementation 證明是 replace 而不是 add,情況少見
if (originalImplementation) {
// 添加 AspectsForwardInvocationSelectorName 的方法功茴,IMP 為原生 forwardInvocation:
class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName), originalImplementation, "v@:@");
}
AspectLog(@"Aspects: %@ is now aspect aware.", NSStringFromClass(klass));
}
上面的方法就是把要 Hook 的目標(biāo) Class 的 forwardInvocation: 混寫了庐冯,混寫之后 forwardInvocation: 的具體實(shí)現(xiàn)在 ASPECTS_ARE_BEING_CALLED 中,里面能看到 invoke 標(biāo)識位的不同是如何實(shí)現(xiàn)的坎穿,還有一些其他的實(shí)現(xiàn)細(xì)節(jié):
// 宏定義肄扎,以便于我們有一個更明晰的 stack trace
#define aspect_invoke(aspects, info) \
for (AspectIdentifier *aspect in aspects) {\
[aspect invokeWithInfo:info];\
if (aspect.options & AspectOptionAutomaticRemoval) { \
aspectsToRemove = [aspectsToRemove?:@[] arrayByAddingObject:aspect]; \
} \
}
static void __ASPECTS_ARE_BEING_CALLED__(__unsafe_unretained NSObject *self, SEL selector, NSInvocation *invocation) {
// __unsafe_unretained NSObject *self 不解釋了
// 斷言 self, invocation
NSCParameterAssert(self);
NSCParameterAssert(invocation);
// 從 invocation 可以拿到很多東西,比如 originalSelector
SEL originalSelector = invocation.selector;
// originalSelector 加前綴得到 aliasSelector
SEL aliasSelector = aspect_aliasForSelector(invocation.selector);
// 用 aliasSelector 替換 invocation.selector
invocation.selector = aliasSelector;
// Instance 的容器
AspectsContainer *objectContainer = objc_getAssociatedObject(self, aliasSelector);
// Class 的容器
AspectsContainer *classContainer = aspect_getContainerForClass(object_getClass(self), aliasSelector);
AspectInfo *info = [[AspectInfo alloc] initWithInstance:self invocation:invocation];
NSArray *aspectsToRemove = nil;
// Before hooks.
aspect_invoke(classContainer.beforeAspects, info);
aspect_invoke(objectContainer.beforeAspects, info);
// Instead hooks.
BOOL respondsToAlias = YES;
if (objectContainer.insteadAspects.count || classContainer.insteadAspects.count) {
// 如果有任何 insteadAspects 就直接替換了
aspect_invoke(classContainer.insteadAspects, info);
aspect_invoke(objectContainer.insteadAspects, info);
}else { // 否則正常執(zhí)行
// 遍歷 invocation.target 及其 superClass 找到實(shí)例可以響應(yīng) aliasSelector 的點(diǎn) invoke
Class klass = object_getClass(invocation.target);
do {
if ((respondsToAlias = [klass instancesRespondToSelector:aliasSelector])) {
[invocation invoke];
break;
}
}while (!respondsToAlias && (klass = class_getSuperclass(klass)));
}
// After hooks.
aspect_invoke(classContainer.afterAspects, info);
aspect_invoke(objectContainer.afterAspects, info);
// 如果沒有 hook赁酝,則執(zhí)行原始實(shí)現(xiàn)(通常會拋出異常)
if (!respondsToAlias) {
invocation.selector = originalSelector;
SEL originalForwardInvocationSEL = NSSelectorFromString(AspectsForwardInvocationSelectorName);
// 如果可以響應(yīng) originalForwardInvocationSEL,表示之前是 replace method 而非 add method
if ([self respondsToSelector:originalForwardInvocationSEL]) {
((void( *)(id, SEL, NSInvocation *))objc_msgSend)(self, originalForwardInvocationSEL, invocation);
}else {
[self doesNotRecognizeSelector:invocation.selector];
}
}
// 移除 aspectsToRemove 隊(duì)列中的 AspectIdentifier旭等,執(zhí)行 remove
[aspectsToRemove makeObjectsPerformSelector:@selector(remove)];
}
#undef aspect_invoke
Note: aspect_invoke 宏定義的作用域酌呆。
- 代碼實(shí)現(xiàn)對應(yīng)了 Hook 的
AspectOptions
參數(shù)的 Before,Instead 和 After搔耕。 - aspect_invoke 中
aspectsToRemove
是一個 NSArray隙袁,里面容納著需要被銷戶的 Hook,即AspectIdentifier
(之后會調(diào)用 remove 移除)弃榨。 - 遍歷
invocation.target
及其 superClass 找到實(shí)例可以響應(yīng) aliasSelector 的點(diǎn) invoke 實(shí)現(xiàn)代碼菩收。
3.3 Block Hook
Aspects 讓我們在指定 Class 或 Instance 的特定 Selector 執(zhí)行時,根據(jù) AspectOptions 插入我們自己的 Block 做 Hook鲸睛,而這個 Block 內(nèi)部有我們想要的有關(guān)于當(dāng)前 Target 和 Selector 的信息娜饵,我們來看一下 Aspects 是怎么辦到的
- (BOOL)invokeWithInfo:(id)info {
NSInvocation *blockInvocation = [NSInvocation invocationWithMethodSignature:self.blockSignature];
NSInvocation *originalInvocation = info.originalInvocation;
NSUInteger numberOfArguments = self.blockSignature.numberOfArguments;
// 偏執(zhí)。我們已經(jīng)在 hook 注冊的時候檢查過了官辈,(不過這里我們還要檢查)箱舞。
if (numberOfArguments > originalInvocation.methodSignature.numberOfArguments) {
AspectLogError(@"Block has too many arguments. Not calling %@", info);
return NO;
}
// block 的 `self` 將會是 AspectInfo∪冢可選的晴股。
if (numberOfArguments > 1) {
[blockInvocation setArgument:&info atIndex:1];
}
// 簡歷參數(shù)分配內(nèi)存 argBuf 然后從 originalInvocation 取 argument 賦值給 blockInvocation
void *argBuf = NULL;
for (NSUInteger idx = 2; idx < numberOfArguments; idx++) {
const char *type = [originalInvocation.methodSignature getArgumentTypeAtIndex:idx];
NSUInteger argSize;
NSGetSizeAndAlignment(type, &argSize, NULL);
// reallocf 優(yōu)點(diǎn),如果創(chuàng)建內(nèi)存失敗會自動釋放之前的內(nèi)存肺魁,講究
if (!(argBuf = reallocf(argBuf, argSize))) {
AspectLogError(@"Failed to allocate memory for block invocation.");
return NO;
}
[originalInvocation getArgument:argBuf atIndex:idx];
[blockInvocation setArgument:argBuf atIndex:idx];
}
// 執(zhí)行
[blockInvocation invokeWithTarget:self.block];
// 釋放 argBuf
if (argBuf != NULL) {
free(argBuf);
}
return YES;
}
考慮兩個問題:
- [blockInvocation setArgument:&info atIndex:1]; 為什么要在索引 1 處插入呢电湘?
- for (NSUInteger idx = 2; idx < numberOfArguments; idx++) 為什么要從索引 2 開始遍歷參數(shù)呢?
總結(jié)
文章簡單介紹了 AOP 的概念,希望能給各位讀者對 AOP 思想的理解提供微薄的幫助寂呛。
文章系統(tǒng)的剖析了 Aspects 開源庫的內(nèi)部結(jié)構(gòu)怎诫,希望能讓大家在瀏覽 Aspects 源碼時快速定位代碼位置,找到核心內(nèi)容昧谊。
文章重點(diǎn)分析了 Aspects 的核心代碼刽虹,提煉了一些筆者認(rèn)為值得注意的點(diǎn),但愿可以在大家扒源碼時提供一些指引呢诬。
本文參考 從 Aspects 源碼中我學(xué)到了什么涌哲?