功能:修改父类不可修改函数方法,函数方法交换

应用场景:假如我们使用的他人提供一个的framework,.m已被打包成二进制.a无法修改源码,只留下.h头文件,那假如代码中某个函数出现了问题可以通过这样的方法进行修改某个函数

一:利用category进行方法覆盖

我们知道,利用category,可以达到“方法覆盖”的效果:

比如:

//
// Teacher.h #import <Foundation/Foundation.h> @interface Teacher : NSObject
- (void) testMethod;
@end @interface Teacher(LogCategory) @end
//
// Teacher.m #import "Teacher.h" @implementation Teacher
- (void) testMethod{
NSLog(@" >> origin testMethod");
}
@end @implementation Teacher(LogCategory)
- (void) testMethod{
NSLog(@" >> LogCategory testMethod");
}
@end

通过增加category类调用同样的方法,方法是可以被覆盖的。调用Teacher对象的testMethod输出的是“>> LogCategory testMethod”

二:利用Method Swizzling进行方法修改

如果看过Objective-C的消息机制,应该知道OC的方法实现都是动态的,都是基于Runtime上的消息来实现。

概括如下:

我们来看看具体消息发送之后是怎么来动态查找对应的方法的。

首先,编译器将代码[obj makeText],转化为objc_msgSend(obj, @selector (makeText));

在objc_msgSend函数中。首先通过obj的isa指针找到obj对应的class。在Class中先去cache中 通过SEL查找对应函数method(猜测cache中method列表是以SEL为key通过hash表来存储的,这样能提高函数查找速度)。若 cache中未找到,再去methodList中查找,若methodlist中未找到,则取superClass中查找。若能找到,则将method加 入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。

所以可以通过修改 SEL 对应的 Method 的函数指针既可以达到修改消息行为的目的。

代码实现的逻辑是这样:

1.取得 SEL 对应的 Method;
2.修改或交换 Method 的函数指针,在这里是通过系统APImethod_exchangeImplementations()交换实现的。

这里有个需要注意的地方:

"ObjC 中的类(class)和实例(instance)都是对象,类对象有自己的类方法列表,实例对象有自己的实例方法列表,这些方法列表(struct objc_method_list)是存储在 struct objc_class 中的。每个方法列表存储近似 SEL:Method 的对,Method 是一个对象,包含方法的具体实现 impl。"

也就是说,对于类方法和实例方法取得SEL对应的Method函数是不一样的。(比如类方法是Method origMethod = class_getInstanceMethod(self, origSel);)

如下代码就是实现函数交换

.h

#if TARGET_OS_IPHONE
#import <objc/runtime.h>
#import <objc/message.h>
#else
#import <objc/objc-class.h>
#endif #import <Foundation/Foundation.h> @interface NSObject (MethodSwizzlingCategory) + (BOOL)swizzleMethod:(SEL)origSel withMethod:(SEL)altSel;
+ (BOOL)swizzleClassMethod:(SEL)origSel withClassMethod:(SEL)altSel; @end

.m

#import "NSObject+MethodSwizzlingCategory.h"

@implementation NSObject (MethodSwizzlingCategory)

+ (BOOL)swizzleMethod:(SEL)origSel withMethod:(SEL)altSel
{
Method origMethod = class_getInstanceMethod(self, origSel);
if (!origSel) {
NSLog(@"original method %@ not found for class %@", NSStringFromSelector(origSel), [self class]);
return NO;
} Method altMethod = class_getInstanceMethod(self, altSel);
if (!altMethod) {
NSLog(@"original method %@ not found for class %@", NSStringFromSelector(altSel), [self class]);
return NO;
} class_addMethod(self,
origSel,
class_getMethodImplementation(self, origSel),
method_getTypeEncoding(origMethod));
class_addMethod(self,
altSel,
class_getMethodImplementation(self, altSel),
method_getTypeEncoding(altMethod)); method_exchangeImplementations(class_getInstanceMethod(self, origSel), class_getInstanceMethod(self, altSel)); return YES;
} + (BOOL)swizzleClassMethod:(SEL)origSel withClassMethod:(SEL)altSel
{
Class c = object_getClass((id)self);
return [c swizzleMethod:origSel withMethod:altSel];
} @end

具体使用如下:

//
// Teacher.h #import <Foundation/Foundation.h>
#import "NSObject+MethodSwizzlingCategory.h" @interface Teacher : NSObject
- (void) originLog;
@end @interface SuperTeacher:Teacher
- (void) altLog;
@end
//
// Teacher.m #import "Teacher.h" @implementation Teacher - (void) originLog{
NSLog(@" >> origin Log");
}
@end @implementation SuperTeacher
- (void) altLog{
NSLog(@" >> alt Log");
}
@end

实现代码:

    Teacher *tc=[[Teacher alloc]init];
SuperTeacher *stc=[[SuperTeacher alloc]init];
[stc originLog];
[stc altLog]; NSLog(@"========= Method Swizzling test =========");
[SuperTeacher swizzleMethod:@selector(originLog) withMethod:@selector(altLog)];
[tc originLog];
[stc altLog];

输出:

2015-12-08 11:45:47.559 OCTest[2807:150681]  >> origin Log
2015-12-08 11:45:47.560 OCTest[2807:150681] >> alt Log
2015-12-08 11:45:47.560 OCTest[2807:150681] ========= Method Swizzling test =========
2015-12-08 11:45:47.561 OCTest[2807:150681] >> alt Log
2015-12-08 11:45:47.561 OCTest[2807:150681] >> origin Log

说明[stc altlog]消息被指向了调用originLog函数,[stc originlog]消息指向了altlog函数。

参考:

http://blog.csdn.net/kesalin/article/details/7178871

Objective-C 利用OC的消息机制,使用Method Swizzling进行方法修改的更多相关文章

  1. OC的消息机制简单介绍

    在OC的消息机制中主要分为三个阶段,分别为: 1.消息发送阶段:从类以及父类的方法缓存列表和方法列表查找方法. 2.动态解析阶段:在消息发送阶段没有找到方法,则会进入这个阶段,负责动态添加方法实现. ...

  2. NSObject头文件解析 / 消息机制 / Runtime解读 (一)

    NSObject头文件解析 当我们需要自定义类都会创建一个NSObject子类, 比如: #import <Foundation/Foundation.h> @interface Clas ...

  3. OC 对象调用属性或实例变量或方法的细节。

    1.成员变量可以理解为所有在类的头上声明的,无论是@interface.@implementation下用大括号括起来或者是用@property声明的变量都可以称作这个类的 成员变量,只是在@impl ...

  4. 利用OC对象的消息重定向forwardingTargetForSelector方法构建高扩展性的滤镜功能

    在OC中,当像一个对象发送消息,而对象找到消息后,从它的类方法列表,父类方法列表,一直找到根类方法列表都没有找到与这个选择子对应的函数指针.那么这个对象就会触发消息转发机制. OC对象的继承链和isa ...

  5. Android中利用Handler实现消息的分发机制(三)

    在第二篇文章<Android中利用Handler实现消息的分发机制(一)>中,我们讲到主线程的Looper是Android系统在启动App的时候,已经帮我们创建好了,而假设在子线程中须要去 ...

  6. 利用消息机制实现VC与Delphi之间的通讯(发送自定义消息)

    摘要: 本文介绍了使用Windows消息机制实现由不同语言编制的程序之间的相互通讯.联系,并以当前较为流行的两种语言Microsoft Visual C++ 6.0和Borland delphi 5. ...

  7. OC 消息机制本质

    转载自:http://m.blog.csdn.net/blog/util_c/10287909 在Objective-C中,message与方法的真正实现是在执行阶段绑定的,而非编译阶段.编译器会将消 ...

  8. OC 内存管理机制总结

    OC 内存管理机制总结 一:OC内存管理机制目前分为两块,其一自动内存管理机制,其二手动内存管理机制: 1.首先我们从自动内存管理机制讲起: 1)什么是自动内存管理机制,自动内存管理机制就是程序中所创 ...

  9. runtime——消息机制

    本文授权转载,作者:Sindri的小巢(简书) 从异常说起 我们都知道,在iOS中存在这么一个通用类类型id,它可以用来表示任何对象的类型 —— 这意味着我们使用id类型的对象调用任何一个方法,编译器 ...

随机推荐

  1. noi题库(noi.openjudge.cn) 1.5编程基础之循环控制T36——T45

    T36 计算多项式的值 描述 假定多项式的形式为xn+xn-1+-+x2+x+1,请计算给定单精度浮点数x和正整数n值的情况下这个多项式的值. 输入 输入仅一行,包括x和n,用单个空格隔开.x在flo ...

  2. 使用perl实现scp批量分发

    perl模块Net::SCP::Expect批量下发文件 用Net::SSH::Perl和Net::SCP::Expect写部署脚本 scp分发文件的perl脚本 Perl SCP操作 #!/usr/ ...

  3. sobel算子

    #1,个人理解 网上查了很多资料,都说sobel算子是用来检测边缘的,分别给了两个方向上的卷积核,然后说明做法,就说这就是sobel算子.对于我个人来说,还有很多不明白的地方,所以理清下思路. #2, ...

  4. velocity模板引擎学习(3)-异常处理

    按上回继续,前面写过一篇Spring MVC下的异常处理.及Spring MVC下的ajax异常处理,今天看下换成velocity模板引擎后,如何处理异常页面: 一.404错误.500错误 <e ...

  5. SQL 2014 in-memory中的storage部分

    基于CTP1的官方白皮书,自己理解的内容.白皮书下载地址:http://download.microsoft.com/download/F/5/0/F5096A71-3C31-4E9F-864E-A6 ...

  6. 柯尔莫可洛夫-斯米洛夫检验(Kolmogorov–Smirnov test,K-S test)

    柯尔莫哥洛夫-斯米尔诺夫检验(Колмогоров-Смирнов检验)基于累计分布函数,用以检验两个经验分布是否不同或一个经验分布与另一个理想分布是否不同. 在进行cumulative probab ...

  7. linux开机自动启动

    1 .vi /etc/rc.local 2.编写开机后运行的命令 如:service httpd start

  8. python 英文字串首字母改为大写

    #英文字串首字母改为大写 st = "string" St = st[0].upper() + st[1:] 2016-10-22 后来了解到 python 内部有相关实现,感觉  ...

  9. .net MVC全球化资源使用心得

    网上有的我就不说了,我只记录下我碰壁的事情. local资源就不说,这里只说global全局资源文件. 假设新建一个资源文件名称叫做resourceA, 下面几点记录备忘: resouceA就是Get ...

  10. 搭建企业内部yum仓库(centos6+centos7+epel源)

    搭建自己的yum仓库,将自己制作好的rpm包,添加到自己的yum源中. yum仓库服务端配置如下 : 1. 创建yum仓库目录 mkdir -p /data/yum_data/cd /data/yum ...