//
// Aspects.m
// Aspects - A delightful, simple library for aspect oriented programming.
//
// Copyright (c) 2014 Peter Steinberger. Licensed under the MIT license.
//
#import "Aspects.h"
#import <libkern/OSAtomic.h>
#import <objc/runtime.h>
#import <objc/message.h>
#define AspectLog(...)
//#define AspectLog(...) do { NSLog(__VA_ARGS__); }while(0)
#define AspectLogError(...) do { NSLog(__VA_ARGS__); }while(0)
// Block internals.
typedef NS_OPTIONS(int, AspectBlockFlags) {
AspectBlockFlagsHasCopyDisposeHelpers = (1 << 25),
AspectBlockFlagsHasSignature = (1 << 30)
};
// 结构体
typedef struct _AspectBlock {
__unused Class isa;
AspectBlockFlags flags;
__unused int reserved;
void (__unused *invoke)(struct _AspectBlock *block, ...);
struct {
// 1
unsigned long int reserved;
unsigned long int size;
// 2
// requires AspectBlockFlagsHasCopyDisposeHelpers
void (*copy)(void *dst, const void *src);
void (*dispose)(const void *);
// 3
// requires AspectBlockFlagsHasSignature
const char *signature;
const char *layout;
} *descriptor;
// imported variables
} *AspectBlockRef;
// class AspectInfo 遵循了AspectInfo协议
// 一个 Aspect 执行环境,主要是 NSInvocation 信息。
@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
// Tracks a single aspect.
// 一个 Aspect 的具体内容。这里主要包含了单个的 aspect 的具体信息,包括执行时机,要执行 block 所需要用到的具体信息:包括方法签名、参数等等
// AspectIdentifier 实际上是添加切面的 Block 的第一个参数,其应该遵循 AspectToken 协议,事实上也的确如此,其提供了 remove 方法的实现。
// 注意并不是,这里是最开始看的时候搞错了。
/*
我想作者应该是忘记遵守协议了. 实现里面也确实有remove方法
*/
// 实际上是这样的
/*
+ (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对象。而实现里面返回的是 是一个 AspectIdentifier 对象。所以并不是作者忘记遵守协议了。而是这里返回的时候加了。等外面拿到的时候就是一个遵守了协议的id对象了。
*/
@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
// Tracks all aspects for an object/class.
// 一个对象或者类的所有的 Aspects 整体情况。是切面的容器类。关联指定对象的指定方法,内部有3个切面队列,分别容纳关联指定对象的指定方法中相对应的 option 的hoook
@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
// 切面追踪器
@interface AspectTracker : NSObject
- (id)initWithTrackedClass:(Class)trackedClass;
@property (nonatomic, strong) Class trackedClass;
@property (nonatomic, readonly) NSString *trackedClassName;
@property (nonatomic, strong) NSMutableSet *selectorNames;
@property (nonatomic, strong) NSMutableDictionary *selectorNamesToSubclassTrackers;
- (void)addSubclassTracker:(AspectTracker *)subclassTracker hookingSelectorName:(NSString *)selectorName;
- (void)removeSubclassTracker:(AspectTracker *)subclassTracker hookingSelectorName:(NSString *)selectorName;
- (BOOL)subclassHasHookedSelectorName:(NSString *)selectorName;
- (NSSet *)subclassTrackersHookingSelectorName:(NSString *)selectorName;
@end
@interface NSInvocation (Aspects)
- (NSArray *)aspects_arguments;
@end
// 转换成二进制是。0000 0111
#define AspectPositionFilter 0x07
#define AspectError(errorCode, errorDescription) do { \
AspectLogError(@"Aspects: %@", errorDescription); \
if (error) { *error = [NSError errorWithDomain:AspectErrorDomain code:errorCode userInfo:@{NSLocalizedDescriptionKey: errorDescription}]; }}while(0)
NSString *const AspectErrorDomain = @"AspectErrorDomain";
//固定后缀
static NSString *const AspectsSubclassSuffix = @"_Aspects_";
// 固定前缀
static NSString *const AspectsMessagePrefix = @"aspects_";
@implementation NSObject (Aspects)
// Aspects 使用 NSObject + Categroy 的方式提供接口,非常巧妙的涵盖了 Instance 和 Class。
// Aspects 提供的接口保持高度一致
///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Public Aspects API
+ (id<AspectToken>)aspect_hookSelector:(SEL)selector
withOptions:(AspectOptions)options
usingBlock:(id)block
error:(NSError **)error {
return aspect_add((id)self, selector, options, block, error);
}
/// @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 {
return aspect_add(self, selector, options, block, error);
}
///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Private Helper
// 私有 helper
// 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)) { // 判断能否被hook
// 记录数据结构
AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector);
// 初始化 identifier
identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error];
if (identifier) { // 如果identifier存在
// 加到list中
[aspectContainer addAspect:identifier withOptions:options];
// !!! 想来这里是核心。之前的都是在准备和初始化
// Modify the class to allow message interception.
// swizzling
// 修改class允许消息拦截
// swizzling
aspect_prepareClassAndHookSelector(self, selector, error);
}
}
});
return identifier;
}
// aspect_remove
static BOOL aspect_remove(AspectIdentifier *aspect, NSError **error) {
NSCAssert([aspect isKindOfClass:AspectIdentifier.class], @"Must have correct type.");
__block BOOL success = NO;
// 锁
aspect_performLocked(^{
// 持有
id self = aspect.object; // strongify
if (self) {
AspectsContainer *aspectContainer = aspect_getContainerForObject(self, aspect.selector);
// 移除
success = [aspectContainer removeAspect:aspect];
aspect_cleanupHookedClassAndSelector(self, aspect.selector);
// destroy token
// 销毁
aspect.object = nil;
aspect.block = nil;
aspect.selector = NULL;
}else {
NSString *errrorDesc = [NSString stringWithFormat:@"Unable to deregister hook. Object already deallocated: %@", aspect];
AspectError(AspectErrorRemoveObjectAlreadyDeallocated, errrorDesc);
}
});
return success;
}
// 加锁
static void aspect_performLocked(dispatch_block_t block) {
/*
将代码块作为参数,然后对其执行前后加锁,这样做的好处是:如果以后要换锁的类型,那么只要在这个方法中改变就可以了
*/
static OSSpinLock aspect_lock = OS_SPINLOCK_INIT;
OSSpinLockLock(&aspect_lock);
block();
OSSpinLockUnlock(&aspect_lock);
}
// 这个方法,传入一个selector,返回一个加了前缀的selector
static SEL aspect_aliasForSelector(SEL selector) {
NSCParameterAssert(selector);
return NSSelectorFromString([AspectsMessagePrefix stringByAppendingFormat:@"_%@", NSStringFromSelector(selector)]);
}
// 方法签名
static NSMethodSignature *aspect_blockMethodSignature(id block, NSError **error) {
// 首先把block桥接成结构体
AspectBlockRef layout = (__bridge void *)block;
// 判断 layout->flags 是否存在
if (!(layout->flags & AspectBlockFlagsHasSignature)) {
// 如果不存在。
NSString *description = [NSString stringWithFormat:@"The block %@ doesn't contain a type signature.", block];
AspectError(AspectErrorMissingBlockSignature, description);
return nil;
}
// 获取block的descriptor,descriptor结构体的copy或者signature存在着方法签名.但是由于全局block和堆block存在的位置不同,所以需要判断.如果是全局block,则方法签名存在于signature上,如果是堆block,则存在于copy上.
// https://www.jianshu.com/p/1849068b7833
// https://blog.csdn.net/yangbodong22011/article/details/53224856
void *desc = layout->descriptor;
// 默认移动两步 desc指向 copy
desc += 2 * sizeof(unsigned long int);
// 判断layout->flags是否是AspectBlockFlagsHasCopyDisposeHelpers
if (layout->flags & AspectBlockFlagsHasCopyDisposeHelpers) {
// 如果是 再移动两步 desc指向 signature
desc += 2 * sizeof(void *);
}
if (!desc) {
// 如果不存在desc,则抛出异常
NSString *description = [NSString stringWithFormat:@"The block %@ doesn't has a type signature.", block];
AspectError(AspectErrorMissingBlockSignature, description);
return nil;
}
// 转换成 char * signature
const char *signature = (*(const char **)desc);
// 返回签名
return [NSMethodSignature signatureWithObjCTypes:signature];
}
// 是否兼容签名
static BOOL aspect_isCompatibleBlockSignature(NSMethodSignature *blockSignature, id object, SEL selector, NSError **error) {
// 断言
NSCParameterAssert(blockSignature);
NSCParameterAssert(object);
NSCParameterAssert(selector);
// 声明签名匹配为yes
BOOL signaturesMatch = YES;
// obj的方法签名
NSMethodSignature *methodSignature = [[object class] instanceMethodSignatureForSelector:selector];
// 比较numberOfArguments
if (blockSignature.numberOfArguments > methodSignature.numberOfArguments) {
signaturesMatch = NO;
}else {
if (blockSignature.numberOfArguments > 1) {
// 数量大于1时,拿到第。1 个 char。 比较char 的首字母是不是@,不是则signaturesMatch = NO;
const char *blockType = [blockSignature getArgumentTypeAtIndex:1];
if (blockType[0] != '@') {
signaturesMatch = NO;
}
}
// Argument 0 是 self/block 。Argument 1 是 SEL 或者 id<AspectInfo>。我们从位置2开始比较
// Block的arguments可以比method更少。所以以下代码没问题
// Argument 0 is self/block, argument 1 is SEL or id<AspectInfo>. We start comparing at argument 2.
// The block can have less arguments than the method, that's ok.
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 (!signaturesMatch) {
// 如果 signaturesMatch 变为NO,那么抛出错误
NSString *description = [NSString stringWithFormat:@"Block signature %@ doesn't match %@.", blockSignature, methodSignature];
AspectError(AspectErrorIncompatibleBlockSignature, description);
return NO;
}
// return YES
return YES;
}
///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Class + Selector Preparation
// 判断是不是MsgForwardIMP
static BOOL aspect_isMsgForwardIMP(IMP impl) {
return impl == _objc_msgForward
#if !defined(__arm64__)
|| impl == (IMP)_objc_msgForward_stret
#endif
;
}
// 获取消息转发IMP
static IMP aspect_getMsgForwardIMP(NSObject *self, SEL selector) {
IMP msgForwardIMP = _objc_msgForward;
#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) {
@try {
NSUInteger valueSize = 0;
NSGetSizeAndAlignment(encoding, &valueSize, NULL);
if (valueSize == 1 || valueSize == 2 || valueSize == 4 || valueSize == 8) {
methodReturnsStructValue = NO;
}
} @catch (__unused NSException *e) {}
}
if (methodReturnsStructValue) {
msgForwardIMP = (IMP)_objc_msgForward_stret;
}
#endif
return msgForwardIMP;
}
// 准备class and hook selector
static void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector, NSError **error) {
NSCParameterAssert(selector);
Class klass = aspect_hookClass(self, error); //1 swizzling forwardInvocation
// 获取Method
Method targetMethod = class_getInstanceMethod(klass, selector);
// 获取IMP
IMP targetMethodIMP = method_getImplementation(targetMethod);
// 判断是不是消息转发IMP
if (!aspect_isMsgForwardIMP(targetMethodIMP)) { //2 swizzling method
// 混写method. 弄一个这个已经存在的方法的别名。并不是真的copy了一份
// Make a method alias for the existing method implementation, it not already copied.
const char *typeEncoding = method_getTypeEncoding(targetMethod);
// 拿到selector的别名
SEL aliasSelector = aspect_aliasForSelector(selector);
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);
}
// We use forwardInvocation to hook in.
// 使用forwardInvocation hook in
class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);
AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
}
}
// Will undo the runtime changes made.
static void aspect_cleanupHookedClassAndSelector(NSObject *self, SEL selector) {
NSCParameterAssert(self);
NSCParameterAssert(selector);
Class klass = object_getClass(self);
BOOL isMetaClass = class_isMetaClass(klass);
if (isMetaClass) {
klass = (Class)self;
}
// Check if the method is marked as forwarded and undo that.
Method targetMethod = class_getInstanceMethod(klass, selector);
IMP targetMethodIMP = method_getImplementation(targetMethod);
if (aspect_isMsgForwardIMP(targetMethodIMP)) {
// Restore the original method implementation.
const char *typeEncoding = method_getTypeEncoding(targetMethod);
SEL aliasSelector = aspect_aliasForSelector(selector);
Method originalMethod = class_getInstanceMethod(klass, aliasSelector);
IMP originalIMP = method_getImplementation(originalMethod);
NSCAssert(originalMethod, @"Original implementation for %@ not found %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), klass);
class_replaceMethod(klass, selector, originalIMP, typeEncoding);
AspectLog(@"Aspects: Removed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
}
// Deregister global tracked selector
aspect_deregisterTrackedSelector(self, selector);
// Get the aspect container and check if there are any hooks remaining. Clean up if there are not.
AspectsContainer *container = aspect_getContainerForObject(self, selector);
if (!container.hasAspects) {
// Destroy the container
aspect_destroyContainerForObject(self, selector);
// Figure out how the class was modified to undo the changes.
NSString *className = NSStringFromClass(klass);
if ([className hasSuffix:AspectsSubclassSuffix]) {
Class originalClass = NSClassFromString([className stringByReplacingOccurrencesOfString:AspectsSubclassSuffix withString:@""]);
NSCAssert(originalClass != nil, @"Original class must exist");
object_setClass(self, originalClass);
AspectLog(@"Aspects: %@ has been restored.", NSStringFromClass(originalClass));
// We can only dispose the class pair if we can ensure that no instances exist using our subclass.
// Since we don't globally track this, we can't ensure this - but there's also not much overhead in keeping it around.
//objc_disposeClassPair(object.class);
}else {
// Class is most likely swizzled in place. Undo that.
if (isMetaClass) {
aspect_undoSwizzleClassInPlace((Class)self);
}else if (self.class != klass) {
aspect_undoSwizzleClassInPlace(klass);
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Hook Class
// 核心代码
// hook class
static Class aspect_hookClass(NSObject *self, NSError **error) {
// 断言
NSCParameterAssert(self);
// 当self是实例对象的时候,返回的是类对象,否则则返回自身。
Class statedClass = self.class;
// self的isa
Class baseClass = object_getClass(self);
// className
NSString *className = NSStringFromClass(baseClass);
// Already subclassed
// 已经子类化过了
if ([className hasSuffix:AspectsSubclassSuffix]) {
return baseClass;
// We swizzle a class object, not a single object.
// 我们混写了一个类对象,而不是一个单独的object
}else if (class_isMetaClass(baseClass)) {
// baseClass是元类,则self是class 或 metaClass,混写self
return aspect_swizzleClassInPlace((Class)self);
// 可能是一个 KVO 的类,混写就位。也要混写meta classes
// Probably a KVO'ed class. Swizzle in place. Also swizzle meta classes in place.
}else if (statedClass != baseClass) {
// 当不同时混写baseClass
return aspect_swizzleClassInPlace(baseClass);
}
// Default case. Create dynamic subclass.
// 默认情况下,动态创建子类
// 拼接子类后缀
const char *subclassName = [className stringByAppendingString:AspectsSubclassSuffix].UTF8String;
// 尝试用拼接后缀的名称获取isa
Class subclass = objc_getClass(subclassName);
// 如果找不到isa,代表还没有动态创建过这个子类
if (subclass == nil) {
// 创建一个 class pair。 baseClass 作为新类的 superClass,类名为subclassName,
subclass = objc_allocateClassPair(baseClass, subclassName, 0);
if (subclass == nil) { // 返回nil,表示创建失败
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;
}
static NSString *const AspectsForwardInvocationSelectorName = @"__aspects_forwardInvocation:";
static void aspect_swizzleForwardInvocation(Class klass) {
// 断言 klass
NSCParameterAssert(klass);
[NSObject forwardInvocation:nil];
// If there is no method, replace will act like class_addMethod.
// 如果没有method,那么替换的作用和class_addMethod相同
// 这里是把forwardInvocation 的 IMP 替换成 __ASPECTS_ARE_BEING_CALLED__
// 如果是replace返回原forwardInvocation:的IMP的地址
// 之后系统调用 forwardInvocation 时。就会
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));
}
static void aspect_undoSwizzleForwardInvocation(Class klass) {
NSCParameterAssert(klass);
// 这里拿到的是原来forwardInvocation:
Method originalMethod = class_getInstanceMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName));
// 拿到 forwardInvocation:
Method objectMethod = class_getInstanceMethod(NSObject.class, @selector(forwardInvocation:));
// There is no class_removeMethod, so the best we can do is to retore the original implementation, or use a dummy.
IMP originalImplementation = method_getImplementation(originalMethod ?: objectMethod);
// 换回来
class_replaceMethod(klass, @selector(forwardInvocation:), originalImplementation, "v@:@");
AspectLog(@"Aspects: %@ has been restored.", NSStringFromClass(klass));
}
static void aspect_hookedGetClass(Class class, Class statedClass) {
NSCParameterAssert(class);
NSCParameterAssert(statedClass);
// 拿到class method。
Method method = class_getInstanceMethod(class, @selector(class));
// 创建一个IMP
IMP newIMP = imp_implementationWithBlock(^(id self) {
return statedClass;
});
// 替换 @selector(class) 的IMP为 newIMP。就是说。调 class方法时。返回的实际是statedClass
class_replaceMethod(class, @selector(class), newIMP, method_getTypeEncoding(method));
}
///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Swizzle Class In Place
// 混写类
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);
}
}
static Class aspect_swizzleClassInPlace(Class klass) {
NSCParameterAssert(klass);
// 类名
NSString *className = NSStringFromClass(klass);
_aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) {
// 这个swizzledClasses是否包含className
if (![swizzledClasses containsObject:className]) {
// 如果不包含。aspect_swizzleForwardInvocation ,并加入
aspect_swizzleForwardInvocation(klass);
[swizzledClasses addObject:className];
}
});
return klass;
}
static void aspect_undoSwizzleClassInPlace(Class klass) {
NSCParameterAssert(klass);
NSString *className = NSStringFromClass(klass);
_aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) {
if ([swizzledClasses containsObject:className]) {
aspect_undoSwizzleForwardInvocation(klass);
[swizzledClasses removeObject:className];
}
});
}
///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Aspect Invoke Point
// This is a macro so we get a cleaner stack trace.
// 这是一个宏定义用来表示一个更清晰的 堆栈轨迹
#define aspect_invoke(aspects, info) \
for (AspectIdentifier *aspect in aspects) {\
[aspect invokeWithInfo:info];\
if (aspect.options & AspectOptionAutomaticRemoval) { \
aspectsToRemove = [aspectsToRemove?:@[] arrayByAddingObject:aspect]; \
} \
}
// This is the swizzled forwardInvocation: method.
// 这个是混写的forwardInvocation: method.
static void __ASPECTS_ARE_BEING_CALLED__(__unsafe_unretained NSObject *self, SEL selector, NSInvocation *invocation) {
// 两个断言
NSCParameterAssert(self);
NSCParameterAssert(invocation);
// 从invovation可以拿到很多东西 比如 originalSelector
SEL originalSelector = invocation.selector;
// 加前缀
SEL aliasSelector = aspect_aliasForSelector(invocation.selector);
// 替换
invocation.selector = aliasSelector;
// 获取 Instance 的容器
AspectsContainer *objectContainer = objc_getAssociatedObject(self, aliasSelector);
// Class 的容器
AspectsContainer *classContainer = aspect_getContainerForClass(object_getClass(self), aliasSelector);
// info
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 {
// 否则执行下面
Class klass = object_getClass(invocation.target);
// 遍历 invocation.target 及其 superClass 找到实例可以响应 aliasSelector 的 invoke
do {
if ((respondsToAlias = [klass instancesRespondToSelector:aliasSelector])) {
[invocation invoke];
break;
}
}while (!respondsToAlias && (klass = class_getSuperclass(klass)));
}
// After hooks.
aspect_invoke(classContainer.afterAspects, info);
aspect_invoke(objectContainer.afterAspects, info);
// If no hooks are installed, call original implementation (usually to throw an exception)
// 如果没有hook,则执行原实现(通常会抛出异常)
if (!respondsToAlias) {
invocation.selector = originalSelector;
SEL originalForwardInvocationSEL = NSSelectorFromString(AspectsForwardInvocationSelectorName);
// 如果可以响应 originalForwardInvocationSEL,表示之前是 replace method 而非 add method
if ([self respondsToSelector:originalForwardInvocationSEL]) {
((void( *)(id, SEL, NSInvocation *))objc_msgSend)(self, originalForwardInvocationSEL, invocation);
}else {
[self doesNotRecognizeSelector:invocation.selector];
}
}
// 移除 aspectsToRemove 队列中的 AspectIdentifier,执行 remove
// Remove any hooks that are queued for deregistration.
[aspectsToRemove makeObjectsPerformSelector:@selector(remove)];
}
#undef aspect_invoke
// 去除宏定义
///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Aspect Container Management
// Loads or creates the aspect container.
// 加载并创建一个aspect container
static AspectsContainer *aspect_getContainerForObject(NSObject *self, SEL selector) {
NSCParameterAssert(self);
// 获取别名selector
SEL aliasSelector = aspect_aliasForSelector(selector);
// 获取关联对象,如果为nil则set关联对象。
AspectsContainer *aspectContainer = objc_getAssociatedObject(self, aliasSelector);
if (!aspectContainer) {
aspectContainer = [AspectsContainer new];
objc_setAssociatedObject(self, aliasSelector, aspectContainer, OBJC_ASSOCIATION_RETAIN);
}
return aspectContainer;
}
// 传入selector 和class
static AspectsContainer *aspect_getContainerForClass(Class klass, SEL selector) {
// 断言
NSCParameterAssert(klass);
// 定义一个
AspectsContainer *classContainer = nil;
// 遍历找classContainer
do {
classContainer = objc_getAssociatedObject(klass, selector);
if (classContainer.hasAspects) break;
}while ((klass = class_getSuperclass(klass)));
return classContainer;
}
static void aspect_destroyContainerForObject(id<NSObject> self, SEL selector) {
NSCParameterAssert(self);
SEL aliasSelector = aspect_aliasForSelector(selector);
objc_setAssociatedObject(self, aliasSelector, nil, OBJC_ASSOCIATION_RETAIN);
}
///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Selector Blacklist Checking
static NSMutableDictionary *aspect_getSwizzledClassesDict() {
// 单例,初始化
static NSMutableDictionary *swizzledClassesDict;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
swizzledClassesDict = [NSMutableDictionary new];
});
return swizzledClassesDict;
}
// 检查允许
static BOOL aspect_isSelectorAllowedAndTrack(NSObject *self, SEL selector, AspectOptions options, NSError **error) {
// 黑名单
static NSSet *disallowedSelectorList;
static dispatch_once_t pred;
// 一个单例
dispatch_once(&pred, ^{
// 初始化。
disallowedSelectorList = [NSSet setWithObjects:@"retain", @"release", @"autorelease", @"forwardInvocation:", nil];
});
// Check against the blacklist.
NSString *selectorName = NSStringFromSelector(selector);
if ([disallowedSelectorList containsObject:selectorName]) {
// 如果在黑名单里
NSString *errorDescription = [NSString stringWithFormat:@"Selector %@ is blacklisted.", selectorName];
AspectError(AspectErrorSelectorBlacklisted, errorDescription);
return NO;
}
// Additional checks.
// 保证是012中的一个。
AspectOptions position = options&AspectPositionFilter;
if ([selectorName isEqualToString:@"dealloc"] && position != AspectPositionBefore) {
// 如果是dealloc && 不是 before
NSString *errorDesc = @"AspectPositionBefore is the only valid position when hooking dealloc.";
AspectError(AspectErrorSelectorDeallocPosition, errorDesc);
return NO;
}
if (![self respondsToSelector:selector] && ![self.class instancesRespondToSelector:selector]) {
// 如果没有这个selector
NSString *errorDesc = [NSString stringWithFormat:@"Unable to find selector -[%@ %@].", NSStringFromClass(self.class), selectorName];
AspectError(AspectErrorDoesNotRespondToSelector, errorDesc);
return NO;
}
// Search for the current class and the class hierarchy IF we are modifying a class object
if (class_isMetaClass(object_getClass(self))) {
// 如果是元类
Class klass = [self class];
NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict();
Class currentClass = [self class];
// 这里先找看能不能从swizzledClassesDict中找到这个class对应的tracker。
// 分为两种情况
/*
1.为nil。则初始化。并赋值。swizzledClassesDict中。key是class
2.不为nil。则抛出异常。返回NO。
*/
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];
AspectError(AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription);
return NO;
}
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 method can only be hooked once per class hierarchy.", selectorName, NSStringFromClass(currentClass)];
AspectError(AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription);
return NO;
}
} while ((currentClass = class_getSuperclass(currentClass)));
// Add the selector as being modified.
currentClass = klass;
AspectTracker *subclassTracker = nil;
do {
tracker = swizzledClassesDict[currentClass];
if (!tracker) {
tracker = [[AspectTracker alloc] initWithTrackedClass:currentClass];
swizzledClassesDict[(id<NSCopying>)currentClass] = tracker;
}
if (subclassTracker) {
[tracker addSubclassTracker:subclassTracker hookingSelectorName:selectorName];
} else {
[tracker.selectorNames addObject:selectorName];
}
// All superclasses get marked as having a subclass that is modified.
subclassTracker = tracker;
}while ((currentClass = class_getSuperclass(currentClass)));
} else {
return YES;
}
return YES;
}
static void aspect_deregisterTrackedSelector(id self, SEL selector) {
if (!class_isMetaClass(object_getClass(self))) return;
NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict();
NSString *selectorName = NSStringFromSelector(selector);
Class currentClass = [self class];
AspectTracker *subclassTracker = nil;
do {
AspectTracker *tracker = swizzledClassesDict[currentClass];
if (subclassTracker) {
[tracker removeSubclassTracker:subclassTracker hookingSelectorName:selectorName];
} else {
[tracker.selectorNames removeObject:selectorName];
}
if (tracker.selectorNames.count == 0 && tracker.selectorNamesToSubclassTrackers) {
[swizzledClassesDict removeObjectForKey:currentClass];
}
subclassTracker = tracker;
}while ((currentClass = class_getSuperclass(currentClass)));
}
@end
@implementation AspectTracker
- (id)initWithTrackedClass:(Class)trackedClass {
if (self = [super init]) {
_trackedClass = trackedClass;
_selectorNames = [NSMutableSet new];
_selectorNamesToSubclassTrackers = [NSMutableDictionary new];
}
return self;
}
- (BOOL)subclassHasHookedSelectorName:(NSString *)selectorName {
return self.selectorNamesToSubclassTrackers[selectorName] != nil;
}
- (void)addSubclassTracker:(AspectTracker *)subclassTracker hookingSelectorName:(NSString *)selectorName {
NSMutableSet *trackerSet = self.selectorNamesToSubclassTrackers[selectorName];
if (!trackerSet) {
trackerSet = [NSMutableSet new];
self.selectorNamesToSubclassTrackers[selectorName] = trackerSet;
}
[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;
}
- (NSString *)trackedClassName {
return 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
///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - NSInvocation (Aspects)
@implementation NSInvocation (Aspects)
// 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
}
- (NSArray *)aspects_arguments {
NSMutableArray *argumentsArray = [NSMutableArray array];
for (NSUInteger idx = 2; idx < self.methodSignature.numberOfArguments; idx++) {
[argumentsArray addObject:[self aspect_argumentAtIndex:idx] ?: NSNull.null];
}
return [argumentsArray copy];
}
@end
///////////////////////////////////////////////////////////////////////////////////////////
#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);
// 根据block生成方法签名
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 = [AspectIdentifier new];
identifier.selector = selector;
identifier.block = block;
identifier.blockSignature = blockSignature;
identifier.options = options;
identifier.object = object; // weak
}
return identifier;
}
- (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.
if (numberOfArguments > 1) {
[blockInvocation setArgument:&info atIndex:1];
}
void *argBuf = NULL;
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 getArgument:argBuf atIndex:idx];
[blockInvocation setArgument:argBuf atIndex:idx];
}
[blockInvocation invokeWithTarget:self.block];
if (argBuf != NULL) {
free(argBuf);
}
return YES;
}
// 描述
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p, SEL:%@ object:%@ options:%tu block:%@ (#%tu args)>", self.class, self, NSStringFromSelector(self.selector), self.object, self.options, self.block, self.blockSignature.numberOfArguments];
}
// 这里是代理方法。
- (BOOL)remove {
// 实际调用了 aspect_remove
return aspect_remove(self, NULL);
}
@end
///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - AspectsContainer
@implementation AspectsContainer
// 是否有aspect
- (BOOL)hasAspects {
return self.beforeAspects.count > 0 || self.insteadAspects.count > 0 || self.afterAspects.count > 0;
}
// 加入aspect
- (void)addAspect:(AspectIdentifier *)aspect withOptions:(AspectOptions)options {
NSParameterAssert(aspect);
NSUInteger position = options&AspectPositionFilter;
// 这里做一个判断,加入不同的list
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 ?:@[]) arrayByAddingObject:aspect]; break;
}
}
// 移除aspect
- (BOOL)removeAspect:(id)aspect {
for (NSString *aspectArrayName in @[NSStringFromSelector(@selector(beforeAspects)),
NSStringFromSelector(@selector(insteadAspects)),
NSStringFromSelector(@selector(afterAspects))]) {
NSArray *array = [self valueForKey:aspectArrayName];
NSUInteger index = [array indexOfObjectIdenticalTo:aspect];
if (array && index != NSNotFound) {
NSMutableArray *newArray = [NSMutableArray arrayWithArray:array];
[newArray removeObjectAtIndex:index];
[self setValue:newArray forKey:aspectArrayName];
return YES;
}
}
return NO;
}
// 描述
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p, before:%@, instead:%@, after:%@>", self.class, self, self.beforeAspects, self.insteadAspects, self.afterAspects];
}
@end
///////////////////////////////////////////////////////////////////////////////////////////
#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 {
// Lazily evaluate arguments, boxing is expensive.
if (!_arguments) {
_arguments = self.originalInvocation.aspects_arguments;
}
return _arguments;
}
@end