浅谈Objc之AOP编程

AOP编程,就是切片编程,是切入到函数实现(函数代码块实现)的编程。问题场景:当你想在某个已存在函数代码块实现之前或之后,添加另一部分额外功能时,但又不想直接将代码插入(这部分代码相对独立,插进去总觉得和原来代码是逻辑无关的),也不想通过父类统一添加(有可能创建多个父类?);看样子,在编译阶段解决此问题,有些难度;在运行阶段有办法吗?有,就是AOP。

既然说的是OC中的AOP,就直说OC中如何实现的。

先说下原理:在OC中,不管类方法还是实例方法,”方法声明“用的是选择器类型记录的,即SEL类型,通常叫选择器名;方法都有自己的实现,像在c或c++中,方法名指向的就是函数入口(函数实现入口),OC不是这样的,他是通过选择器名索引到对应的实现,然后执行实现代码。

回到问题场景,如果我们能改变选择器名对应的实现地址,让他对应你那那段独立的代码实现,然后定义另外一个选择器名,让他对应原来那段代码实现,这样,程序运行时,就在不改变源代码的基础上完成了额外工作。需要注意的是,由于我们自己定义了新的选择器名,让他对应原来那段代码实现,所以在执行完额外功能后,需要在独立的代码实现后发送新的选择器消息。
 

项目中,拥有这中需求的类应该不止一个,所以我将交换函数实现的功能,放在NSObject分类中。这样哪个类需要,就在自己分类中的类方法发load中实现即可。下面举了UIViewController和UIControl俩个类的交换函数实现。

//
//  NSObject+AOP.h
//

#import <Foundation/Foundation.h>

@interface NSObject (AOP)

/**
 交换方法名对应的实现

 @param newSelector 新方法名
 @param oldSelector 老方法名
 */
+(void)exChangeMethodImpWithNewSelctor:(SEL)newSelector oldSelctor:(SEL)oldSelector;

@end
//
//  NSObject+AOP.m
//

#import "NSObject+AOP.h"
#import <objc/runtime.h>

@implementation NSObject (AOP)

/**
 交换方法名对应的实现(这样所有的NSObject子类都可以调用,谁调用self就是谁)
 
 @param newSelector 新选择器方法名
 @param oldSelector 老选择器方法名
 */
+(void)exChangeMethodImpWithNewSelctor:(SEL)newSelector oldSelctor:(SEL)oldSelector{
    
    Method newMethod = class_getInstanceMethod([self class], newSelector);
    Method oldMethod = class_getInstanceMethod([self class], oldSelector);
    
    method_exchangeImplementations(newMethod, oldMethod);
}

@end
//
//  UIViewController+AOP.h
//

#import <UIKit/UIKit.h>

@interface UIViewController (AOP)

@end
//
//  UIViewController+AOP.m
//

#import "UIViewController+AOP.h"
#import "NSObject+AOP.h"

@implementation UIViewController (AOP)

//系统在加载类的时候,会自动调用load方法,且只调用一次。所以,在这里做aop操作。
+(void)load{
    SEL newSelector = @selector(aop_viewWillAppear:);
    SEL oldSelector = @selector(viewWillAppear:);
    
    [self exChangeMethodImpWithNewSelctor:newSelector oldSelctor:oldSelector];
}

-(void)aop_viewWillAppear:(BOOL)animated{
    NSLog(@"替换后是%s", __func__);
    //调用原来实现
    [self aop_viewWillAppear:animated];
}

@end
//
//  UIControl+AOP.h
//

/*
 iOS中大多数可点击UI控件都是UIControl的子类,而所有的事件也都要通过 - (void)sendAction:(SEL)action to:(nullable id)target forEvent:(nullable UIEvent *)event转发,在这个方法里,我们也可以获取该控件所有的信息,包括所在的控制器,坐标系等,为配置埋点做依据。

 这样我们就有了完美的swizzling对象了。通过该方法,(据我不完全统计)我们可以监听到UIButton、UITextField、UISwitch、UISegmentedControl、UISlider 、UIStepper等控件的Action事件。
 */

#import <UIKit/UIKit.h>

@interface UIControl (AOP)

@end
//
//  UIControl+AOP.m
//

#import "UIControl+AOP.h"
#import "NSObject+AOP.h"

@implementation UIControl (AOP)

//系统在加载类的时候,会自动调用load方法,且只调用一次。所以,在这里最aop操作。
+(void)load{
    SEL newSelector = @selector(aop_sendAction:to:forEvent:);
    SEL oldSelector = @selector(sendAction:to:forEvent:);
    
    [self exChangeMethodImpWithNewSelctor:newSelector oldSelctor:oldSelector];
}

- (void)aop_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{
    NSLog(@"替换后是%s", __func__);
    NSLog(@"self class=%@---target class=%@", NSStringFromClass([self class]), NSStringFromClass([target class]));
    //调用原来实现
    [self aop_sendAction:action to:target forEvent:event];
}

@end

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值