涉及到的知识点有:消息转发TODO,Method-SwizzlingTODO,动态方法TODO,KVO(暂无),AOP等。
下载地址
概述
Aspects是一个轻量级的、支持Objective-C&Swift语言的AOP实现。可以把Aspects当做Method-SwizzlingTODO的一种应用。它允许你在每个类或每个实例已有方法中添加代码,可以设置切入点为after /instead/before等。比起常规的Method-SwizzlingTODO,Aspects会自动调用super方法,并且使用起来更加简单、方便。
Aspects利用OC的消息转发机制,hook消息。这样会有一些性能开销,因此不要把Aspects加到经常被使用的方法里面。Aspects是用来设计给View/Controller代码使用的,而不是用来hook每秒调用1000次的方法的。因此Aspects不应该被用在for循环这些方法里面,会造成很大的性能损耗。
Aspects是不支持hook 静态static方法的。
这里还有一些简介需要翻译,后续补上。TODO
使用方法
Aspects是NSObject的一个extension,只要是NSObject,都可以使用这两个方法。一个是用来hook类方法的,一个是hook实例方法的。
/// 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`, 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`.
///
/// @note Hooking static methods is not supported.
/// @return A token which allows to later deregister the aspect.
+ (id)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)aspect_hookSelector:(SEL)selector
withOptions:(AspectOptions)options
usingBlock:(id)block
error:(NSError **)error;
添加Aspects后,会返回一个AspectToken,用来注销hook方法。所有的调用都是线程安全的。
/// Deregister an aspect.
/// @return YES if deregistration is successful, otherwise NO.
id aspect = ...;
[aspect remove];
这里的AspectToken是隐式的,允许我们调用remove去撤销一个hook。remove方法返回YES代表撤销成功,返回NO就撤销失败。
Hook方法的参数
参数名 | 类型 | 含义 |
---|---|---|
selector | SEL | 要Hook的方法,即增加切面的原方法 |
options | AspectOptions | 切入点 |
block | id | 这个block复制了正在被hook的方法的签名signature类型。 |
error | NSError ** | 返回的错误 |
block第一个参数遵循AspectInfo协议。我们甚至可以使用一个空的block。AspectInfo协议里面的参数是可选的,主要是用来匹配block签名的。
AspectOptions类型:
typedef NS_OPTIONS(NSUInteger, AspectOptions) {
AspectPositionAfter = 0, /// Called after the original implementation (default);默认值,在原方法执行完之后调用block
AspectPositionInstead = 1, /// Will replace the original implementation.替换原方法
AspectPositionBefore = 2, /// Called before the original implementation.在原方法之前调用block
AspectOptionAutomaticRemoval = 1 << 3 /// Will remove the hook after the first execution.在hook执行完后自动移除。
};
AspectInfo对象:
/// The AspectInfo protocol is the first parameter of our block syntax.
@protocol AspectInfo
/// The instance that is currently hooked.
/// 返回当前被hook的实例。
- (id)instance;
/// The original invocation of the hooked method.
/// 返回被hooked方法的原始的Invocation。
- (NSInvocation *)originalInvocation;
/// All method arguments, boxed. This is lazily evaluated.
/// 返回selector方法的所有参数。是懒加载实现的。
- (NSArray *)arguments;
@end
错误码类型:
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.
};
extern NSString *const AspectErrorDomain;
.m分析TODO