在没有一个类的实现源码的情况下,想改变其中一个方法的实现,除了继承它重写、和借助类别重名方法暴力抢先之外,还有就是方法交换

方法交换的原理:在OC中调用一个方法其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用OC的动态特性,可以实现在运行时偷换selector方法的实现,达到和方法挂钩的目的。

每一个类都有一个方法列表,存放在selector的名字和方法实现的映射关系,imp有点像函数指针,指向具体的方法实现。

可以利用method_exchanggeimplementations来交换两个方法的imp

可以利用class_replaceMethod来替换方法的imp

可以利用method_setimplementation来直接设置某个方法的imp

归根结底方法交换就是偷换了selector的imp

Method Swizzling 实践

举个例子好了,就替换NSArray的objectAtIndex:方法吧,只需两个步骤。

第一步:自定义一个objectAtIndex:方法,方法名不能和系统的一样

#import <objc/runtime.h>
#import "NSArray+Swizzle.h"
@implementation NSArray (Swizzle)
-(id)ll_ObjectAtIndex:(NSUInteger)index{ if (index < [self count]) { return [self ll_ObjectAtIndex:index]; }else{ return nil; } }
乍一看,这不递归了么?别忘记这是我们准备调换IMP的selector,[self ll_ObjectAtIndex:index] 将会执行真的 [self objectAtIndex:index] 。 
第二步:调换IMP
+(void)initialize{

    static dispatch_once_t once;

    dispatch_once(&once, ^{

        Class class = NSClassFromString(@"__NSArrayI");

        [self swizzleMethods:class originalSelector:@selector(objectAtIndex:) swizzledSelector:@selector(ll_objectAtIndex:)];

    });

}

//方法交换的具体实现

+ (void)swizzleMethods:(Class)class originalSelector:(SEL)origSel swizzledSelector:(SEL)swizSel

{

    Method origMethod = class_getInstanceMethod(class, origSel);

    Method swizMethod = class_getInstanceMethod(class, swizSel);

    //class_addMethod will fail if original method already exists

    BOOL didAddMethod = class_addMethod(class, origSel, method_getImplementation(swizMethod), method_getTypeEncoding(swizMethod));

    if (didAddMethod) {

        class_replaceMethod(class, swizSel, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));

    } else {

        //origMethod and swizMethod already exist

        method_exchangeImplementations(origMethod, swizMethod);

    }

}

@end  
定义完毕新方法后,需要弄清楚什么时候实现与系统的方法交互?
既然是给系统的方法添加额外的功能,换句话说,我们以后在开发中都是使用自己定义的方法,取代系统的方法,所以,当程序一启动,就要求能使用自己定义的功能方法.说到这里:我们必须要弄明白一下两个方法 :
+(void)initialize(当类第一次被调用的时候就会调用该方法,整个程序运行中只会调用一次)
+ (void)load(当程序启动的时候就会调用该方法,换句话说,只要程序一启动就会调用load方法,整个程序运行中只会调用一次)
方法交换可以做什么呢
1、防止数组越界
2、统计页面的访问次数(通过交换viewdidload 或者 viewwillappear等方法)
当然还有很多其他的用途,这些只是较为常见的


												

最新文章

  1. [转载]跨域iframe高度自适应
  2. vSphere6提示已弃用VMFS卷的解决方法
  3. C#高级特性_Lambda
  4. javascript 数组的深度复制
  5. 关于华擎X99+5820K
  6. IP配置
  7. RGPJS 教程之八 创造场景
  8. CentOS 6.4 升级 Mysq5.5l方法 和 用户远程登录数据库
  9. 【Oracle&amp;SQLServer】并集、交际、补集
  10. MYSQL小常识
  11. angular的数据双向绑定秘密
  12. C_数据结构
  13. sql trim()函数去掉两头空格
  14. 几个SQL语句(备忘)
  15. centos7安装mysql(yum)
  16. spring的基本使用
  17. java基础语法(一)
  18. WPF 禁用TextBox的触摸后自动弹出虚拟键盘
  19. 第13组_16通信3班_045_OSPFv3作业
  20. Linux下获取java堆栈文件并进行分析

热门文章

  1. DirectX9完全面向对象框架
  2. Vue(十)---路由
  3. 使用 json 模块,使json数据格式与Python字典dict数据格式互相转换,获取数据更加方便
  4. 007.Oracle数据库 , 使用%进行模糊查询
  5. mysql 视图入门
  6. openssl生成CA签署 及 加密解密基础
  7. 解决fedora28桌面图标问题
  8. 六、Vue-Router:基础路由处理、路由提取成单独文件、路由嵌套、路由传参数、路由高亮、html5的history使用
  9. 九九乘法表的四种三角形排布方式(for循环以及while循环的互换)
  10. apache启动错误:Could not reliably determine the server&#39;s fully qualified domain name