• Objective-C 方法交换实践(三)


    一、类与变量

    AspectOptions
    typedef NS_OPTIONS(NSUInteger, AspectOptions) {
    
        AspectPositionAfter   = 0,            /// 原方法之后 (default)
    
        AspectPositionInstead = 1,            /// 替换原方法
    
        AspectPositionBefore  = 2,            /// 原方法之前
    
        AspectOptionAutomaticRemoval = 1 << 3 /// 执行一次后就移除
    
    };
    
    
    AspectsContainer

    存储AspectIdentifier ,总共有三个数组,分别存储 上面的 After Instead Before 的各种 Aspects。

    AspectIdentifier
    + (instancetype)identifierWithSelector:(SEL)selector object:(id)object options:(AspectOptions)options block:(id)block error:(NSError **)error;
    

    从创建方法就可以看出,他是用来记录 要替换的对象object,替换的原方法selector,替换的类型options,以及执行的代码block。其中block的参数是 id<AspectInfo>类型的。

    AspectInfo

    一种是协议名,标记当前 NSinvocation的一些环境

    @protocol AspectInfo <NSObject>
    
    /// The instance that is currently hooked.
    - (id)instance;
    
    /// The original invocation of the hooked method.
    - (NSInvocation *)originalInvocation;
    
    /// All method arguments, boxed. This is lazily evaluated.
    - (NSArray *)arguments;
    
    @end
    

    一种是类名,基本上就是实现上面的协议的类。

    @interface AspectInfo : NSObject <AspectInfo>
    - (id)initWithInstance:(__unsafe_unretained id)instance invocation:(NSInvocation *)invocation;
    @property (nonatomic, unsafe_unretained, readonly) id instance;
    @property (nonatomic, strong, readonly) NSArray *arguments;
    @property (nonatomic, strong, readonly) NSInvocation *originalInvocation;
    @end
    

    二、具体过程

    1.aspect_isSelectorAllowedAndTrack 检测是否能够swizzle

    检测不能是以下方法 @"retain", @"release", @"autorelease", @"forwardInvocation:"

    对于"dealloc"方法位置只能是 AspectPositionBefore

    被交换的方法是否未实现

    如果是metaClass,如果子类已经hook,返回;如果父类已经hook,返回。否者标记hook的 SEL,为父类标记已经hook 的 Child Class。

    2.aspect_getContainerForObject

    得到当前 select 对应的 AspectsContainer, 不存在就创建,通过关联对象的方式存储在类中

    3.AspectIdentifier

    创建 AspectIdentifier 并且添加到AspectsContainer里面去

    4.aspect_prepareClassAndHookSelector 进行swizzle操作
    • 如果已经操作过,返回

    • 如果是metaClass,对metaClass 进行 A操作

    • 如果实例对象进行过 object_setClass 操作,对 class 进行A操作

    • 否则创建一个 subclass ,对其进行 object_setClass 操作,然后对这个 subclass 进行 A 操作,之后把 当前对象的 isa 指向 subclass

    A操作是:

    替换类的 forwardInvocation 方法为 __ASPECTS_ARE_BEING_CALLED__

    为类增加 aliasSelector 指向原 selector

    把原 selector 指向 _objc_msgForward 或者_objc_msgForward_stret

    当然里面还有一些容错判断

    5.__ASPECTS_ARE_BEING_CALLED__ 流程

    取出当前对象(object)的 AspectsContainer 和 类(class)的 AspectsContainer ,调用 Before hooks 的一些方法,调用 instead 的方法,调用 after 的方法

    对没有instead的情况检测是否有原方法,没有就走原 forwardInvocation方法或者走 doesNotRecognizeSelector 方法

  • 相关阅读:
    微信公众平台开发心得笔记
    MFC中 在线程里获取主对话框上编辑框中的内容
    MFC中 使用Tab Control 控件在对话框中添加属性页
    MFC中 将std::string转换为LPCTSTR的方法
    MFC中 给按钮添加图片的方法
    MFC中 使用多线程实现语音播放功能
    MFC中 Group Box 控件会覆盖掉部分其他控件
    MFC中 CSting与std::string互相转换
    MFC中 给对话框添加背景图片
    MFC中 通过对子对话框的操作来更新主对话框
  • 原文地址:https://www.cnblogs.com/v2m_/p/9174686.html
Copyright © 2020-2023  润新知