Aspects 源代码解析<一>
2015-10-04 20:33
465 查看
Aspects 是什么,解决了什么问题?
Aspects是AOP(面向切面编程)思想在iOS下OC的实现。Aspects可以用于hook函数,让函数执行一些副操作(打印调试信息、记录日志等)。切面可以简单理解为嵌入不同函数中的功能相同的操作(打印调试信息等),每类功能相同的操作可以抽取出一个切面。下面简要介绍OOP(面向对象编程)和AOP的概念和区别:OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。
AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
OOP实际上是对对象的属性和行为的封装,而AOP对于这点就无从谈起,但是AOP是处理某个步骤和阶段的,从中进行切面的提取,也就是说,如果几个或更多个逻辑过程中,有重复的操作行为,AOP就可以提取出来,运用动态代理,实现程序功能的统一维护,这么说来可能太含蓄,如果说到权限判断,日志记录等,可能就明白了。如果我们单纯使用OOP,那么权限判断怎么办?在每个操作前都加入权限判断?日志记录怎么办?在每个方法里的开始、结束、异常的地方手动添加日志?所有,如果使用AOP就可以借助代理完成这些重复的操作,就能够在逻辑过程中,降低各部分之间的耦合了。二者扬长补短,互相结合最好。
Aspects的思路及实现
思路
Aspects对外暴露了两个接口,用于hook selector:#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); }
参数说明:
selector | options | block | error |
---|---|---|---|
要被hook的selector | 切面(block)执行的时机,在selector执行前、后执行,还是替换被hook的selector | 切面 | hook过程中的错误 |
第一个方法为类方法,receiver为要被hook的类(
[receiver message])。直接在本类上hook类的实例方法(被KVO过的类,直接在KVO过程中生成的子类上进行hook实例方法),进行method swizzling,对类的methodLists
struct objc_method_list **methodLists进行修改,修改的方法有两个:
forwardInvocation:
forwardInvocation: 的IMP(方法实现)被替换为:
__ASPECTS_ARE_BEING_CALLED__,该函数内部具体执行被hook的selector和切入的操作,forwardInvocation: 的本来的IMP被保存在
__aspects_forwardInvocation:中。forwardInvocation:的method swizzling操作是在
static Class aspect_hookClass(NSObject *self, NSError **error)中进行的;
被hook的selector。被hook的selector的IMP被替换为:
_objc_msgForward/
_objc_msgForward_stret,该方法用于触发消息转发,被hook的selector的本来的IMP被保存在
aliasSelector中。被hook的selector 的method swizzling 是在
static void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector, NSError **error)中进行的。
第二个方法为实例方法,receiver为要被hook的类的实例。用该方法hook实例方法较复杂,步骤简述如下:
新生成一个被hook类的子类,
利用isa swizzle将该实例的isa
Class isa指向新生成的被hook类的子类,
对被hook类的子类进行method swizzling,method swizzling的思路与上面类方法的method swizzling相同。
这里直接拿世健同学的例子来讲解吧:
@interface Target : NSObject - (void)targetSelector; @end - (void)targetSelector { NSLog(@"%@'s original selector: %@", self, NSStringFromSelector(_cmd)); }
我们要对Target类的targetSelector进行hook,以方法二(实例方法)为例进行讲解,假定aTarget为Target类的一个实例方法。
Target *aTarget = [[Target alloc] init]; [aTarget aspect_hookSelector:NSSelectorFromString(@"targetSelector") withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> aspectInfo) { NSLog(@"Hook targetSelector"); } error:NULL]; [aTarget targetSelector];
hook之前aTarget及targetSelector的指向:
hook的过程:
hook Class(“isa swizzling”):
通过statedClass = self.class获取self本来的class(class方法被重写了,用来获取self被hook之前的Class(Target)。下文交代class方法是如何被重写的);
通过
Class baseClass = object_getClass(self)获取self的isa指针实际指向的class(self在运行时实际的class,表面上看这是一只皮鞋(statedClass),实际上这是一只刮胡刀(basedClass))。
如果baseClass(实际指向的class)已经是被hook过的子类,则返回baseClass。
如果baseClass是MetaClass或者被KVO过的Class,则不必再生成subClass,直接在其自身上进行method swizzling。
如果不是上述3. 、4. 所述情况,默认情况下需要对被hook的Class进行”isa swizzling”:
通过
subclass = objc_allocateClassPair(baseClass, subclassName, 0)动态创建一个被hook类(
Target)的子类(
Target_Aspects_);
然后对子类的
forwardInvocation:进行method swizzling,替换为
__ASPECTS_ARE_BEING_CALLED__,进行消息转发时,实际执行的是
__ASPECTS_ARE_BEING_CALLED__中的方法;
重写子类的获取类名的方法
class,使其返回被hook之前的类的类名;
将self(aTarget)的isa指针指向子类
Target_Aspects_(
object_setClass(self, subclass))。
class被hook后的情况:
#pragma mark - Hook Class static Class aspect_hookClass(NSObject *self, NSError **error) { NSCParameterAssert(self); Class statedClass = self.class; Class baseClass = object_getClass(self); NSString *className = NSStringFromClass(baseClass); // Already subclassed if ([className hasSuffix:AspectsSubclassSuffix]) { return baseClass; // We swizzle a class object, not a single object. }else if (class_isMetaClass(baseClass)) { return aspect_swizzleClassInPlace((Class)self); // Probably a KVO'ed class. Swizzle in place. Also swizzle meta classes in place. }else if (statedClass != baseClass) { return aspect_swizzleClassInPlace(baseClass); } // Default case. Create dynamic subclass. const char *subclassName = [className stringByAppendingString:AspectsSubclassSuffix].UTF8String; Class subclass = objc_getClass(subclassName); if (subclass == nil) { subclass = objc_allocateClassPair(baseClass, subclassName, 0); if (subclass == nil) { NSString *errrorDesc = [NSString stringWithFormat:@"objc_allocateClassPair failed to allocate class %s.", subclassName]; AspectError(AspectErrorFailedToAllocateClassPair, errrorDesc); return nil; } aspect_swizzleForwardInvocation(subclass); aspect_hookedGetClass(subclass, statedClass); aspect_hookedGetClass(object_getClass(subclass), statedClass); objc_registerClassPair(subclass); } object_setClass(self, subclass); return subclass; }
hook selector:
用新生成的aspects__targetSelector指向selector的IMP;将selector指向_objc_msgForward(或者_objc_msgForward_stret,与前者的区别为返回值的不同,该实现返回结构体?)。
selector 被hook后:
经过上面hook class和hook selector,Target的targetSelector就被hook进“切面”了。当调用
[aTarget targetSelector]时,程序流程如下:
参考:
/article/5086208.html 简单理解AOP(面向切面编程)
http://blog.ibireme.com/2013/11/26/objective-c-messaging/ Objective-C 中的消息与消息转发
http://blog.cnbang.net/tech/2855/ JSPatch实现原理详解<二>
相关文章推荐
- Codeforces 583 DIV2 Asphalting Roads 模拟
- Codeforces Round #323 (Div. 2) 583A. Asphalting Roads
- codeforces #323 div 2 A. Asphalting Roads(暴力
- ASP.NET连接ACCESS数据库WEB.CONFIG内路径最优写法
- ASP.NET 无法向会话状态服务器发出会话状态请求请。确保 ASP.NET State Service (ASP.NET 状态服务)已启动
- cf#323-div2-A. Asphalting Roads-简单模拟
- Asp.Net MVC HTML Helper 中 LabelFor TextBoxFor的一些理解
- Asp.Net MVC 使用 DataAnnotations 进行模型验证
- Asphalting Roads
- ASP.NET,Web界面利用Button来实现将telerik:RadGrid表格内数据导出Excel
- Codeforces Round #323 A Asphalting Roads
- 树莓派raspberry Pi2 介绍
- 【Jqurey EasyUI+Asp.net】---DataGrid增加、删、更改、搜
- 在树莓派Raspberry Pi 2 Model B 上安装ROS
- 无显示屏安装raspberrypi OS
- Raspberry pi 配置OpenCV环境
- Raspberry pi摄像头的使用
- sublime text 3神奇的插件--snippet
- Spring 之AOP AspectJ切入点语法详解(最全了,不需要再去其他地找了)
- Metasploit渗透技巧:后渗透Meterpreter代理