AOP(Aspect Oriented Programming)面向切片编程
- AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
iOS 如何如何实现面向切片编程
- AOP一般都是需要有一个拦截器,然后在每一个切片运行之前和运行之后(或者任何你希望的地方),通过调用拦截器的方法来把这个jointpoint扔到外面,在外面获得这个jointpoint的时候,执行相应的代码。
- 在iOS开发领域,objective-C的runtime有提供了一系列的方法,能够让我们拦截到某个方法的调用,来实现拦截器的功能,这种手段我们称为Method Swizzling。Aspects通过这个手段实现了针对某个类和某个实例中方法的拦截。
使用场景:
- 统一处理逻辑
- 在不改变源码的情况下,插入源码
- 当我们执行某种方法时候,对方法进行安全检查 (例如,NSArray的数组越界问题)
- 对某些操作进行日志记录
- 对购物车进行交互时候,根据用户的操作,触发建议或者提示
源码解析:
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.
};
这是一个枚举,定义了切片的调用时机
公共接口
#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);
}
Aspects是NSObject的分类 @interface NSObject (Aspects),然后提供了实例方法和类方法
- 参数
-
selector
: 要被hook的selector,这是一个SEL类型 -
options
: 切面的执行时机,也就是设置第三个参数block的调用时 -
block
: 切面 (block 是带有自动变量值的匿名函数,其实block的实质也是Objective-C的对象,所以这里能用id来引用,具体详细解析可以参考《Objective-C高级编程》这一本书) -
error
: hook过程中出现的错误
-
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)) {
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;
}
static void aspect_performLocked(dispatch_block_t block) {
static OSSpinLock aspect_lock = OS_SPINLOCK_INIT;
OSSpinLockLock(&aspect_lock);
block();
OSSpinLockUnlock(&aspect_lock);
}
下个系列的文章后面待续
网友评论