Objective-C 利用OC的消息机制,使用Method Swizzling进行方法修改
功能:修改父类不可修改函数方法,函数方法交换
应用场景:假如我们使用的他人提供一个的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进行方法修改的更多相关文章
- OC的消息机制简单介绍
在OC的消息机制中主要分为三个阶段,分别为: 1.消息发送阶段:从类以及父类的方法缓存列表和方法列表查找方法. 2.动态解析阶段:在消息发送阶段没有找到方法,则会进入这个阶段,负责动态添加方法实现. ...
- NSObject头文件解析 / 消息机制 / Runtime解读 (一)
NSObject头文件解析 当我们需要自定义类都会创建一个NSObject子类, 比如: #import <Foundation/Foundation.h> @interface Clas ...
- OC 对象调用属性或实例变量或方法的细节。
1.成员变量可以理解为所有在类的头上声明的,无论是@interface.@implementation下用大括号括起来或者是用@property声明的变量都可以称作这个类的 成员变量,只是在@impl ...
- 利用OC对象的消息重定向forwardingTargetForSelector方法构建高扩展性的滤镜功能
在OC中,当像一个对象发送消息,而对象找到消息后,从它的类方法列表,父类方法列表,一直找到根类方法列表都没有找到与这个选择子对应的函数指针.那么这个对象就会触发消息转发机制. OC对象的继承链和isa ...
- Android中利用Handler实现消息的分发机制(三)
在第二篇文章<Android中利用Handler实现消息的分发机制(一)>中,我们讲到主线程的Looper是Android系统在启动App的时候,已经帮我们创建好了,而假设在子线程中须要去 ...
- 利用消息机制实现VC与Delphi之间的通讯(发送自定义消息)
摘要: 本文介绍了使用Windows消息机制实现由不同语言编制的程序之间的相互通讯.联系,并以当前较为流行的两种语言Microsoft Visual C++ 6.0和Borland delphi 5. ...
- OC 消息机制本质
转载自:http://m.blog.csdn.net/blog/util_c/10287909 在Objective-C中,message与方法的真正实现是在执行阶段绑定的,而非编译阶段.编译器会将消 ...
- OC 内存管理机制总结
OC 内存管理机制总结 一:OC内存管理机制目前分为两块,其一自动内存管理机制,其二手动内存管理机制: 1.首先我们从自动内存管理机制讲起: 1)什么是自动内存管理机制,自动内存管理机制就是程序中所创 ...
- runtime——消息机制
本文授权转载,作者:Sindri的小巢(简书) 从异常说起 我们都知道,在iOS中存在这么一个通用类类型id,它可以用来表示任何对象的类型 —— 这意味着我们使用id类型的对象调用任何一个方法,编译器 ...
随机推荐
- Node进阶:核心模块http简介
本文摘录自<Nodejs学习笔记>,更多章节及更新,请访问 github主页地址.欢迎加群交流,群号 197339705. http模块概览 大多数nodejs开发者都是冲着开发web s ...
- 项目分布式部署那些事(2):基于OCS(Memcached)的Session共享方案
在不久之前发布了一篇"项目分布式部署那些事(1):ONS消息队列.基于Redis的Session共享,开源共享",因为一些问题我们使用了阿里云的OCS,下面就来简单的介绍和分享下相 ...
- 准标识符(Quasi-dientifier, QI)
Quasi-identifier From Wikipedia, the free encyclopedia Quasi-identifiers are pieces of information t ...
- Apache CXF实现WebService发布和调用
第一种方法:不用导入cxf jars 服务端: 1. 新建Web工程 2.新建接口和实现类.测试类 目录结构图如下: 接口代码: package com.cxf.spring.service; imp ...
- 深入理解Java:注解(Annotation)自定义注解入门
转载:http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html 元注解: 元注解的作用就是负责注解其他注解.Java5.0定义了4个标准 ...
- 第十五章:输入和输出(I/O)
一:流分类 抽象基类:InputStream和Reader 抽象类不能用于创建模板哦! OutputStream和Writer 方向: 以内存为中心! 输入流(读) 输出流(写) 数据 ...
- Git与GitHub到底有什么联系?
Git与GitHub区别 git 是一个软件 版本控制器 分享合并代码, 团队开发 时间机器, 可以获取到任意阶段时间节点开发的代码, 代码找回 git, cvs, bitkeeper, svn 典型 ...
- js中return的用法
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...
- 【转】css布局居中和CSS内容居中区别和对应DIV CSS代码
原文地址:http://www.divcss5.com/jiqiao/j771.shtml css布局居中和CSS内容居中区别和对应DIV CSS代码教程与图文代码案例篇 对于新手来说DIV CSS布 ...
- Java设计模式(五) 工厂模式
1,定义抽象产品类 package com.pattern.factory; import java.util.ArrayList; public abstract class Pizza { Str ...