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

应用场景:假如我们使用的他人提供一个的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. python 测试驱动开发的简单例子

    一.需求分析 需求:一个类 MyClass,有两个功能:add, sub 1.先功能设计 # myclass.py class MyClass(object): # 加法 def add(self): ...

  2. Alpha版本测试报告

    请根据团队项目中软件的需求文档.功能规格说明书和技术规格说明书,写出软件的测试计划.测试过程和测试结果,并回答下述问题. 1. 在测试过程中发现了多少Bug? 2. 你是怎么进行场景测试(scenar ...

  3. Pattern Recognition And Machine Learning读书会前言

    读书会成立属于偶然,一次群里无聊到极点,有人说Pattern Recognition And Machine Learning这本书不错,加之有好友之前推荐过,便发了封群邮件组织这个读书会,采用轮流讲 ...

  4. 【分布式协调器】Paxos的工程实现-cocklebur简介(二)

    Cocklebur集群的工作原理 在集群正常工作时,整个集群只会有一个Leader,其他都是Follower.Client可以注册到某个Follower,当然也可以注册到Leader,为了减轻Lead ...

  5. ASP.NET Web API 实现客户端Basic(基本)认证 之简单实现

    优点是逻辑简单明了.设置简单. 缺点显而易见,即使是BASE64后也是可见的明文,很容易被破解.非法利用,使用HTTPS是一个解决方案. 还有就是HTTP是无状态的,同一客户端每次都需要验证. 实现: ...

  6. 中间件(middlebox)

    Middleboxes (also known as network functions) are systems that perform sophisticated and often state ...

  7. MongoDB数据库GroupBy查询使用Spring-data-mongondb的实现

    以前用MongoDB数据库都是简单的查询,直接用Query就可以,最近项目中用到了分组查询,完全不一样.第一次遇到,搞了好几天终于有点那意思了. 先上代码: import java.math.BigD ...

  8. WPS Office Pro 2016 专业版

    感觉WPS还是不错的,Office安装包太大了.嘻嘻 政府专用正版序列号激活码,可永久有效激活! THUV2-32HH7-6NMHN-PTX7Y-QQCTH WPS Office Pro 2016 专 ...

  9. Go语言interface详解

    interface Go语言里面设计最精妙的应该算interface,它让面向对象,内容组织实现非常的方便,当你看完这一章,你就会被interface的巧妙设计所折服. 什么是interface 简单 ...

  10. Tomcat5通过cardadm.xml启动项目

    配置tomcat5\conf\Catalina\localhost下的   cardadm.xml  ,然后在MyEclipse中,直接启动Tomcat5,不需要部署项目