iOS中的“面向切面”式编程

AOP编程这种概念,很少在iOS客户端这里提起过,简单的解释一下:AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。


我们先来看一下iOS这种“动态代理”的实现方式,有关NSProxy的部分,我就不多说了,大家可以参考一下这篇博客 iOS的动态代理模式的实现
我们来学习另一种方法,使用runtime's method-swizzle来实现
先介绍几个方法

[methodExchange]
12345678910111213141516
 OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2)__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

这个方法主要是用于交换两个方法的实现

[methodObtain]
1234567891011121314
 OBJC_EXPORT Methodclass_getInstanceMethod(Class cls, SEL name) __OSX_AVAILABLE_STARTING(__MAC_10_0,__IPHONE_2_0);

这个主要是根据class名称和selector得到Method
有了上面的两个方法,我们就可以交换某个class下的某个方法的实现,当然,方法不一定出自同一个class。 我们来直接上代码,比如我们想把UIView 的initWithFrame:方法交换成我们自定义的一个方法st_initWithFrame:,我们只需要得到两个方法的实现,然后调用 method_exchangeImplementations 方法即可。为了方便,我就直接把替换的过程写在load方法里面了,如下代码所示

[UIView+swizzleInitialize]
12345678910111213141516
@interface UIView (STSwizzle) - (instancetype) st_initWithFrame:(CGRect) frame;@end @implementation UIView (STSwizzle) - (instancetype) st_initWithFrame:(CGRect)frame { NSLog(@"===before invoke initWithFrame=="); id instance = [selfst_initWithFrame:frame]; NSLog(@"===after invoke initWithFrame=="); returninstance; } @end

我们在使用前交换这两个方法就可以了,注意,这个交换是一个全局的,你这里交换了之后,其他地方使用的过程中也会被交换。交换代码如下

[UIViewTest]
123456789101112131415161718192021222324252627
@interface ViewController @end @interface ViewController () @end @implementationViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib.method_exchangeImplementations(class_getInstanceMethod(UIView.class,@selector(initWithFrame:)), class_getInstanceMethod(UIView.class,@selector(st_initWithFrame:))); UIView * view = [[UIView alloc]initWithFrame:self.view.bounds]; view.backgroundColor = UIColor.redColor;[self.view addSubview:view]; } - (void)didReceiveMemoryWarning { [superdidReceiveMemoryWarning]; // Dispose of any resources that can be recreated. }@end

上述的代码就完成了两个方法的交换,留心观察的同学会发现,我在自定义的st_initWithFrame:方法里面又调用了一次方法自身,从静态代码来看,像是死循环,我可以很负责的告诉大家,#### 上述代码如果未发生交换,确实是死循环,但是我们在后面对方法进行了交换,运行期间,我们方法内执行的st_initWithFrame其实已经被交换成原来的initWithFrame:方法了,虚惊一场,大家不要担心,可以自己多做做实验。如果有人运行了例子之后,就可以看到打印的日志如下

[打印的日志]
12
2014-10-23 17:10:40.542 STAOPDemo[17792:816954] ===before invoke initWithFrame==2014-10-23 17:10:40.543 STAOPDemo[17792:816954] ===after invoke initWithFrame==

这说明我们确实交换了方法,并且也初步看到了一种面向切面的新思路,我们没有改变调用方式以及原有提供的接口方法,我们只是新增了category,并且交换了方法而已,绿色无公害,不需要修改原有代码,我们可以把上述交换过程的代码完全放到UIView中,这样对调用者来说就是透明的,如下代码所示,但是注意哦,使用如下所示的方式会直接连系统新建的view的都交换了,所以慎重

[init]
1234567891011121314151617181920
@interface UIView (STSwizzle) - (instancetype) st_initWithFrame:(CGRect) frame;@end @implementation UIView (STSwizzle) + (void) load {method_exchangeImplementations(class_getInstanceMethod(self,@selector(initWithFrame:)), class_getInstanceMethod(self,@selector(st_initWithFrame:))); } - (instancetype) st_initWithFrame:(CGRect) frame{ NSLog(@"===before invoke initWithFrame=="); id instance = [selfst_initWithFrame:frame]; NSLog(@"===after invoke initWithFrame=="); returninstance; } @end

这里再次提醒一下,method_exchangeImplementations 方法可以多次被调用,如果你交换两次,就又交换回来了,你们可以自己试一下,如果要用的过程中,没有自信的话,可以增加变量来控制是否交换。
以上就完成了一种iOS的动态代理,类似于面向切面编程,其中用到了runtime的一些简单的知识,我们可以在原有东西执行之前/之后插入一些自定义的东西,大家可以自己做一做实验。Demo可以通过github 下载。
下面提供一个我这里的使用场景,场景是这样的,虽然系统的imageNamed可以根据当前设备自动查找@2x.png/.png图片,但是568h出现之后,有的地方我们需要使用568h的图片,这里我们就通过这种面向切面的方式,在系统的imageNamed:之前,去寻找合适的,存在的 imageName,然后调用系统的imageNamed。代码会在后续过程中放出,大家可以自己尝试实现。

本文简短网址: http://suenblog.duapp.com/b/100010/ 转摘来源

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值