Aspects 是什么,解决了什么问题?

转载链接:http://blog.csdn.net/wangzengdi/article/details/48881277

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);
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

参数说明:

selector options block error
要被hook的selector 切面(block)执行的时机,在selector执行前、后执行,还是替换被hook的selector 切面 hook过程中的错误

方法说明:

  1. 第一个方法为类方法,receiver为要被hook的类([receiver message])。直接在本类上hook类的实例方法(被KVO过的类,直接在KVO过程中生成的子类上进行hook实例方法),进行method swizzling,对类的methodListsstruct objc_method_list **methodLists进行修改,修改的方法有两个:

    1. forwardInvocation:  
      forwardInvocation: 的IMP(方法实现)被替换为:__ASPECTS_ARE_BEING_CALLED__,该函数内部具体执行被hook的selector和切入的操作,forwardInvocation: 的本来的IMP被保存在__aspects_forwardInvocation:中。forwardInvocation:的method swizzling操作是在static Class aspect_hookClass(NSObject *self, NSError **error)中进行的;
    2. 被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)中进行的。
  2. 第二个方法为实例方法,receiver为要被hook的类的实例。用该方法hook实例方法较复杂,步骤简述如下:

    1. 新生成一个被hook类的子类,
    2. 利用isa swizzle将该实例的isa Class isa指向新生成的被hook类的子类,
    3. 对被hook类的子类进行method swizzling,method swizzling的思路与上面类方法的method swizzling相同。

这里直接拿世健同学的例子来讲解吧:

@interface Target : NSObject
- (void)targetSelector;
@end

- (void)targetSelector
{
    NSLog(@"%@'s original selector: %@", self, NSStringFromSelector(_cmd));
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

我们要对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];
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

hook之前aTarget及targetSelector的指向: 
hook之前aTarget及targetSelector的指向

hook的过程:

hook Class(“isa swizzling”):
  1. 通过statedClass = self.class获取self本来的class(class方法被重写了,用来获取self被hook之前的Class(Target)。下文交代class方法是如何被重写的);
  2. 通过Class baseClass = object_getClass(self)获取self的isa指针实际指向的class(self在运行时实际的class,表面上看这是一只皮鞋(statedClass),实际上这是一只刮胡刀(basedClass))。
  3. 如果baseClass(实际指向的class)已经是被hook过的子类,则返回baseClass。
  4. 如果baseClass是MetaClass或者被KVO过的Class,则不必再生成subClass,直接在其自身上进行method swizzling。
  5. 如果不是上述3. 、4. 所述情况,默认情况下需要对被hook的Class进行”isa swizzling”: 
    1. 通过subclass = objc_allocateClassPair(baseClass, subclassName, 0)动态创建一个被hook类(Target)的子类(Target_Aspects_);
    2. 然后对子类的forwardInvocation:进行method swizzling,替换为__ASPECTS_ARE_BEING_CALLED__,进行消息转发时,实际执行的是__ASPECTS_ARE_BEING_CALLED__中的方法;
    3. 重写子类的获取类名的方法class,使其返回被hook之前的类的类名;
    4. 将self(aTarget)的isa指针指向子类Target_Aspects_object_setClass(self, subclass))。

class被hook后的情况: 
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;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
hook selector:
  1. 用新生成的aspects__targetSelector指向selector的IMP;
  2. 将selector指向_objc_msgForward(或者_objc_msgForward_stret,与前者的区别为返回值的不同,该实现返回结构体?)。

selector 被hook后:

selector 被hook后

经过上面hook class和hook selector,Target的targetSelector就被hook进“切面”了。当调用[aTarget targetSelector]时,程序流程如下:

hook后[aTarget targetSelector]的调用流程

参考: 
http://www.cnblogs.com/jyh317/p/3834271.html 简单理解AOP(面向切面编程) 
http://blog.ibireme.com/2013/11/26/objective-c-messaging/ Objective-C 中的消息与消息转发 
http://blog.cnbang.net/tech/2855/ JSPatch实现原理详解<二>

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值