Aspects的源码学习,我学到的有几下几点

  1. Objective-C Runtime
  2. 理解OC的消息分发机制
  3. KVO中的指针交换技术
  4. Block 在内存中的数据结构
  5. const 的修饰区别
  6. block 中常量在特定情况下的三种处理方法
  7. 断言语句,
  8. 自旋锁 使用注意
  9. _objc_msgForward_stret 和 _objc_msgForward 前者存在的必要

  10. Type Encoding

https://www.cnblogs.com/DafaRan/p/8192069.html

简介

Aspects是一个面向切面编程的库。
如果想深入了解iOS Runtime中的消息发送机制,Aspects的源码是值得分析的。 

项目主页
Aspects

整体分析

阅读Aspects的源码需要以下知识作为基础

  1. Objective-C Runtime
  2. 理解OC的消息分发机制
  3. KVO中的指针交换技术

阅读本文之前,建议应该先断点调试下Aspects的Demo,了解大致的过程。

核心实现

Aspects的核心实现就是利用Runtime中的消息分发机制如图:

Aspects通过把selector的方法替换为msg_forward方法转发 转而调用 forwardInvocation(forwardInvocation的实现被Aspects替换,将原来的方法实现与添加的实现组合在了一起)

核心源码分析

这是Aspects 面向切面编程的入口方法

- (id<AspectToken>)aspect_hookSelector:(SEL)selector
                      withOptions:(AspectOptions)options
                       usingBlock:(id)block
                            error:(NSError **)error {
    return aspect_add(self, selector, options, block, error);
}复制代码

这段代码可以分三部分来看

  1. aspect_isSelectorAllowedAndTrack 这个方法 对父子类同时hook一个方法进行了一些限制
  2. aspect_getContainerForObject 通过Runtime添加关联值的方式 管理hook的方法
  3. aspect_prepareClassAndHookSelector 这是核心的实现,涉及到动态生成子类,改变isa指针的指向,改变方法的实现 一系列操作
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)) {
            //一个实例 只有一个container
            //这是区分实例对象和类对象的关键
            //实例对象可以有很多个,但是同一个类的类对象只能有一个
            AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector);
            //原来的selector block
            identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error];
            if (identifier) {
                //container 里 存有 identifier (selector,block)
                [aspectContainer addAspect:identifier withOptions:options];

                // Modify the class to allow message interception.
                aspect_prepareClassAndHookSelector(self, selector, error);
            }
        }
    });
    return identifier;
}复制代码

核心方法

aspect_prepareClassAndHookSelector这是核心的实现,涉及到动态生成子类,改变isa指针,改变方法的实现 一系列操作

static void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector, NSError **error) {
    NSCParameterAssert(selector);
    //动态创建子类,改变forwardInvocation方法的实现
    Class klass = aspect_hookClass(self, error);
    Method targetMethod = class_getInstanceMethod(klass, selector);
    IMP targetMethodIMP = method_getImplementation(targetMethod);

    if (!aspect_isMsgForwardIMP(targetMethodIMP)) {
        // Make a method alias for the existing method implementation, it not already copied.
        const char *typeEncoding = method_getTypeEncoding(targetMethod);
        SEL aliasSelector = aspect_aliasForSelector(selector);
        if (![klass instancesRespondToSelector:aliasSelector]) {
            //子类的aliasSelector的实现为 当前类的selector
            __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);
        }

        //selector方法替换为_objc_msgForward
        // We use forwardInvocation to hook in.
        class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);
        AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
    }
}复制代码

动态生成子类,改变isa指针

#pragma mark - Hook Class

static Class aspect_hookClass(NSObject *self, NSError **error) {
    NSCParameterAssert(self);
    //这里可以思考一下 class 方法 和 isa 的区别
    //[self class] KVO可能改变了isa指针的指向
    Class statedClass = self.class;

    // object_getClass 能准确的找到isa指针
    Class baseClass = object_getClass(self);
    NSString *className = NSStringFromClass(baseClass);

    // Already subclassed
    //如果已经子类化了 就返回
    if ([className hasSuffix:AspectsSubclassSuffix]) {
        return baseClass;

        //如果是类 就改掉类的forwardInvocation 而不是一个子类对象
        // We swizzle a class object, not a single object.
    }else if (class_isMetaClass(baseClass)) {
        return aspect_swizzleClassInPlace((Class)self);

        //考虑到KVO,KVO的底层实现,交换了isa指针
        // 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;
        }
        // forwardInvocation 替换成 (IMP)_ASPECTS_ARE_BEING_CALLED__
        aspect_swizzleForwardInvocation(subclass);


        //子类的class方法返回当前被hook的对象的class
        aspect_hookedGetClass(subclass, statedClass);
        aspect_hookedGetClass(object_getClass(subclass), statedClass);

        objc_registerClassPair(subclass);
    }

    //将当前self设置为子类,这里其实只是更改了self的isa指针而已, 这里hook了子类的forwardInvocation方法,再次使用当前类时,其实是使用了子类的forwardInvocation方法。
    object_setClass(self, subclass);
    return subclass;
}复制代码

 


作者:Junyiii
链接:https://juejin.im/post/58f6d1675c497d006ca638fe
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
 
 

Aspects初始化工作核心部分的解析

aspect_add方法:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
;
;
;
//声明AspectIdentifier实例
;
{
{
//获得当前aspects__XX方法的AspectsContainer容器
;
//初始化AspectIdentifier变量,方法内部通过toll-free bridged获取Block方法签名,并判断其兼容性
;
{
//通过options选项分别添加到容器中的beforeAspects,insteadAspects,afterAspects这三个数组
;
 
//HookSelector的过程和HookClass的过程
;
}
}
;
;
}

HookClass过程:

 
 
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
42
{
;
;
;
;
 
// 如果类名有_Aspects_前缀,说明Class已被Hook
{
;
 
// 判断是否为类对象,如果是,则直接在当前类中进行swizzle
{
;
// 判断是否为KVO过的对象,因为KVO的对象ISA指针会指向一个中间类,则直接在这个中间类中进行swizzle
{
;
}
 
// 默认则会动态创建一个子类
;
;
 
{
;
{
;
;
;
}
//实现替换当前类forwardInvocation方法的实现为__ASPECTS_ARE_BEING_CALLED__
;
//实现当前类的isa指针指向原生的类
;
//实现当前类的元类的isa指针指向原生的类
;
//注册当前类
;
}
//将当前对象的isa指针指向刚生成的类
;
;
}

HookSelector过程

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
;
//HookClass过程
;
//此时的klass类为刚创建的具有_Aspects_后缀的子类,在创建的时候指定类他的父类,所以我们可以获取到selector这个方法
;
;
//判断是否为消息转发
{
//获得原生方法的类型编码
;
;
{
//为klass添加aspects__XX方法,方法的实现为原生方法的实现。
;
;
}
// 将原生方法实现替换为_objc_msgForward或_objc_msgForward_stret,用来实现消息转发
;
;
}
}

 

Aspects执行工作核心部分的解析

当我们正式向某个接受者发送消息的时候,会进行消息转发,而之前HookClass的过程当中我们已经对forwardInvocation的实现替换为了__ASPECTS_ARE_BEING_CALLED__

 
 
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
42
43
44
45
46
47
48
49
50
51
52
53
54
{
;
;
//获取原始方法XX
;
//获取含有前缀的方法aspects_XX
;
//替换Sel
;
//获得实例对象容器
;
//获得类对象容器
;
//初始化AspectInfo,传入self、invocation参数
;
;
 
// 执行before切入点的调用
;
;
 
// 执行Instead切入点的调用,判断当前insteadAspects是否有数据,如果没有数据则判断当前继承链是否能响应aspects_XX方法,如果能,则直接调用,此时的aspects_XX则为原生的实现。
;
{
;
;
{
;
{
{
;
;
}
;
}
 
// 执行after切入点的调用
;
;
 
// 若Hook未被正确执行,则调用原生消息转发。
{
;
;
{
;
{
;
}
}
 
// 对需要被移除的切面执行remove方法
;
}

执行block的代码

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
//根据blockSignature获取Invocation
;
//获取原生方法的Invocation
;
//获取blockInvocation参数个数
;
 
// 判断blockInvocation参数个数是否大于originalInvocation参数个数
{
;
;
}
 
// blockInvocation给索引为1的参数赋值
{
;
}
//当所以大于1的时候进行遍历,把原生的参数值赋值给相应的blockInvocation中的参数
;
idx

流程图:

iOS 开源库系列 Aspects核心源码分析---面向切面编程之疯狂的 Aspects

 

http://ios.jobbole.com/84522/

相关文章:

  • 2021-08-14
  • 2022-01-15
  • 2021-05-22
  • 2022-12-23
  • 2021-05-14
  • 2021-07-26
  • 2021-08-13
  • 2021-12-18
猜你喜欢
  • 2021-07-09
  • 2022-12-23
  • 2021-06-25
  • 2021-09-11
  • 2021-11-19
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案