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

应用场景:假如我们使用的他人提供一个的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. silverlight: http请求的GET及POST示例

    http请求的get/post并不是难事,只是silverlight中一切皆是异步,所以代码看起来就显得有些冗长了,下面这个HttpHelper是在总结 园友 的基础上,修改得来: namespace ...

  2. Java 位运算(移位、位与、或、异或、非)

    Java提供的位运算符有:左移( << ).右移( >> ) .无符号右移( >>> ) .位与( & ) .位或( | ).位非( ~ ).位异或( ...

  3. Nginx文件类型错误解析漏洞--攻击演练

    今天看书看到其中提到的一个漏洞,那就是Nginx+PHP的服务器中,如果PHP的配置里 cgi.fix_pathinfo=1 那么就会产生一个漏洞.这个配置默认是1的,设为0会导致很多MVC框架(如T ...

  4. Zepto的天坑汇总

    前言 最近在做移动端开发,用的是zepto,发现他跟jquery比起来称之为天坑不足为过,但是由于项目本身原因,以及移动端速度要求的情况下,也只能继续用下去. 所以在这里做一下汇总 对img标签空sr ...

  5. web安全——目录

    说明 写这个目录是为了方便阅读.也是为了记录统一的问题. 这个系列,并不一定是全的,也不一定是对的,所以请大家多做过滤. 这里面场景比较多的是本人在实践中遇到的问题,然后自己思考抽象的. 目录 web ...

  6. 离散系统频响特性函数freqz()

    MATLAB提供了专门用于求离散系统频响特性的函数freqz(),调用freqz()的格式有以下两种: l        [H,w]=freqz(B,A,N) B和A分别为离散系统的系统函数分子.分母 ...

  7. exce生成随机数

    有时候数据库没有数据,造数据专用. 来源于:http://jingyan.baidu.com/article/93f9803feba1f5e0e46f55f2.html 首先介绍一下如何用RAND() ...

  8. Struts2.3.4.1+Spring3.2.3+Hibernate4.1.9整合

    java教程|Struts2.3.4.1+Spring3.2.3+Hibernate4.1.9整合教程并测试成功一.创建项目二.搭建struts-2.3.4.11.struts2必须的Jar包(放到W ...

  9. 天朝git的使用

    开源中国社区 官方网站 https://git.oschina.net/ 开源中国社区成立于2008年8月,其目的是为中国的IT技术人员提供一个全面的.快捷更新的用来检索开源软件以及交流使用开源经验的 ...

  10. 【POJ 2774】Long Long Message 最长公共子串

    还是模板啊,手残&&打成||查错查了1h+TAT #include<cstdio> #include<cstring> #include<algorith ...