Method Swizzling
学习博客:《iOS黑魔法-Method Swizzling》 (这个作者太牛了,写了我一直想知道的类簇的swizz方法)
前言:
使用另一份实现来替换原有的方法实现,这道工序叫做“方法调配”,开发者常用此技术向原有实现中添加新功能。
一、 一般的swizz
先给要替换的方法的类添加一个Category,然后在Category中的+(void)load方法中添加Method Swizzling方法,我们用来替换的方法也写在这个Category中。由于load类方法是程序运行时这个类被加载到内存中就调用的一个方法,执行比较早,并且不需要我们手动调用。而且这个方法具有唯一性,也就是只会被调用一次,不用担心资源抢夺的问题。
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class]; SEL originalSelector = @selector(showSplash:);
SEL swizzledSelector = @selector(swizzledShowSplash:);
// 通过class_getInstanceMethod()函数从当前对象中的method list获取method结构体,如果是类方法就使用class_getClassMethod()函数获取。
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
/**
* 我们在这里使用class_addMethod()函数对Method Swizzling做了一层验证,如果self没有实现被交换的方法,会导致失败。
* 而且self没有交换的方法实现,但是父类有这个方法,这样就会调用父类的方法,结果就不是我们想要的结果了。
* 所以我们在这里通过class_addMethod()的验证,如果self实现了这个方法,class_addMethod()函数将会返回NO,我们就可以对其进行交换了。
*/ BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}else{
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
- (void)swizzledShowSplash:(NSString *)message {
if(message && message.length > ){
[self swizzledShowSplash]; // 此时swizzledShowSplash已经和showSplash交换了实现,所以这里实际是在调用showSplash
}
}
二、Method Swizzling类簇
按照上面的例子对NSArray、NSMutableArray、NSDictionary、NSMutableDictionary等类进行Method Swizzling来做swizz。发现Method Swizzling根本就不起作用。
这是因为Method Swizzling对NSArray这些的类簇是不起作用的。因为这些类簇类,其实是一种抽象工厂的设计模式。抽象工厂内部有很多其它继承自当前类的子类,抽象工厂类会根据不同情况,创建不同的抽象对象来进行使用。例如我们调用NSArray的objectAtIndex:方法,这个类会在方法内部判断,内部创建不同抽象类进行操作。
所以也就是我们对NSArray类进行操作其实只是对父类进行了操作,在NSArray内部会创建其他子类来执行操作,真正执行操作的并不是NSArray自身,所以我们应该对其“真身”进行操作。
类 | “真身” |
NSArray |
__NSArrayI |
NSMutableArray |
__NSArrayM |
NSDictionary |
__NSDictionaryI |
NSMutableArray |
__NSDictionaryM |
例如:防止NSArray因为调用objectAtIndex:方法,取下标时数组越界导致的崩溃的swizz方法:
#import "NSArray+CRMArray.h" @implementation NSArray (CRMArray)
+ (void)load {
[super load];
Method fromMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
Method toMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(lxz_objectAtIndex:));
method_exchangeImplementations(fromMethod, toMethod);
} - (id)lxz_objectAtIndex:(NSUInteger)index {
if (self.count- < index) {
// 这里做一下异常处理,不然都不知道出错了。
@try {
return [self lxz_objectAtIndex:index];
}
@catch (NSException *exception) {
// 在崩溃后会打印崩溃信息,方便我们调试。
NSLog(@"---------- %s Crash Because Method %s ----------\n", class_getName(self.class), __func__);
NSLog(@"%@", [exception callStackSymbols]);
return nil;
}
@finally {}
} else {
return [self lxz_objectAtIndex:index];
}
}
@end
三、使用场景
通过此方案,开发者可以为那些“完全不知道其具体实现的”黑盒方法添加日志记录功能,这非常有助于调试程序。然而,此做法只在调试程序时用,很少有人在调试程序之外的场合用上述“方法调配技术”来永久改动某个类的功能。
Method Swizzling的更多相关文章
- runtime 第四部分method swizzling
接上一篇 http://www.cnblogs.com/ddavidXu/p/5924597.html 转载来源http://www.jianshu.com/p/6b905584f536 http:/ ...
- Objective-C Runtime 运行时之四:Method Swizzling
理解Method Swizzling是学习runtime机制的一个很好的机会.在此不多做整理,仅翻译由Mattt Thompson发表于nshipster的Method Swizzling一文. Me ...
- 【原】iOS动态性(三) Method Swizzling以及AOP编程:在运行时进行代码注入
概述 今天我们主要讨论iOS runtime中的一种黑色技术,称为Method Swizzling.字面上理解Method Swizzling可能比较晦涩难懂,毕竟不是中文,不过你可以理解为“移花接木 ...
- Objective-C 利用OC的消息机制,使用Method Swizzling进行方法修改
功能:修改父类不可修改函数方法,函数方法交换 应用场景:假如我们使用的他人提供一个的framework,.m已被打包成二进制.a无法修改源码,只留下.h头文件,那假如代码中某个函数出现了问题可以通过这 ...
- Method Swizzling (方法调配)
Method Swizzling是改变一个selector的实际实现的技术.通过这一技术,我们可以在运行时通过修改类的分发表中selector对应的函数,来修改方法的实现. 例如,我们想跟踪在程序中每 ...
- iOS中AOP与Method Swizzling 项目中的应用
引子:项目中需要对按钮点击事件进行统计分析,现在项目中就是在按钮的响应代码中添加点击事件,非常繁琐.所以使用了AOP(面向切面编程),将统计的业务逻辑统一抽离出来. 项目中添加的开源库:https:/ ...
- Method Swizzling和AOP(面向切面编程)实践
Method Swizzling和AOP(面向切面编程)实践 参考: http://www.cocoachina.com/ios/20150120/10959.html 上一篇介绍了 Objectiv ...
- iOS运行时与method swizzling
C语言是静态语言,它的工作方式是通过函数调用,这样在编译时我们就已经确定程序如何运行的.而Objective-C是动态语言,它并非通过调用类的方 法来执行功能,而是给对象发送消息,对象在接收到消息之后 ...
- IOS 开发之 Method Swizzling + Category
ios 分类中如果增加的方法与被扩展的类方法名重复,则原方法就没法被调用….看以下例子 例如: @interface ClassA : NSObject - (NSString *) myMethod ...
随机推荐
- JavaSE基础01
JavaSE基础篇01 ------从今天开始,我就学习正式java了,O(∩_∩)O哈哈~,请大家多指教哦 一.Windows常见的dos命令 操作dos命令: win7 --->开始 --- ...
- Excel 回归分析
1 分析两个变量和一个因变量的关系 降水,温度与生长的关系曲线 降水是连续的数,温度有三个温室,每个温室一个温度,生长也是连续的数. 作图的方法是将降水放在一列,然后生长根据温度放三列,同一个温度的放 ...
- .NET 获取客户端的操作系统版本、浏览器版本和IP地址
我们在使用.NET做网站的时候,很多情况下需要需要知道客户端的操作系统版本和浏览器版本,怎样获取客户端的操作系统和浏览器版本呢?我们可以通过分析UserAgent来获取. .NET 获取客户端的操作系 ...
- 【Java EE 学习 31】【JavaScript基础增强】【Ajax基础】【Json基础】
一.JavaScript基础增强 1.弹窗 (1)使用window对象的showModelDialog方法和showModelessDialog方法分别可以弹出模式窗口和非模式窗口,但是只能在IE中使 ...
- python莫名其妙的yield, yield from, yield.send
练了几行代码, 慢慢找感觉. TASK,多线程,异步,很多地方都用到的呢. #!/usr/bin/env python # -*- coding: utf-8 -*- import time from ...
- You don't have permission to access /index.php on this server
<Directory /> #AllowOverride none #Require all denied</Directory>
- MyBatis - MyBatis Generator 生成的example 如何使用 and or 简单混合查询
简单介绍: Criteria,包含一个Cretiron的集合,每一个Criteria对象内包含的Cretiron之间是由AND连接的,是逻辑与的关系. oredCriteria,Example内有一个 ...
- Android想服务器传图片,透过流的方式。还有读取服务器图片(文件),也通过流的方式。
/** * Created by Administrator on 2016/7/19. */ import android.util.Log; import com.gtercn.asPolice. ...
- CodeForces 444C 分块
题目链接:http://codeforces.com/problemset/problem/444/C 题意:给定一个长度为n的序列a[].起初a[i]=i,然后还有一个色度的序列b[],起初b[i] ...
- 探索摸寻之XCode 快捷键
注释/反注释 Command+/ 模拟器没有Home键 在模拟器的应用界面 Command+Shift+h 返回上一级 (1920,24寸显示器,因此不是分辨率问题)