Runtime 函数 Swizzling 改变OC方法的调度顺序
首先加入一个小知识:
SEL、Method、IMP的含义及区别
在运行时,类(Class)维护了一个消息分发列表来解决消息的正确发送。每一个消息列表的入口是一个方法(Method),这个方法映射了一对键值对,其中键是这个方法的名字(SEL),值是指向这个方法实现的函数指针 implementation(IMP)。
伪代码表示:
Class {
MethodList (
Method{
SEL:IMP;
}
Method{
SEL:IMP;
}
);
};
Method Swizzling就是改变类的消息分发列表来让消息解析时从一个选择器(SEL)对应到另外一个的实现(IMP),同时将原始的方法实现混淆到一个新的选择器(SEL)。
对Swizzling方法封装
//
// NSObject+Swizzling.h
// Swizzling
//
// Created by peter.zhang on 2016/12/14.
// Copyright © 2016年 Peter. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface NSObject (Swizzling)
/**
* Adds a new method to a class with a given name and implementation.
*
* @param originalSelector 原来的方法
* @param swizzledSelector 替换成的方法
*
*/
+ (void)methodSwizzlingWithOriginalSelector:(SEL)originalSelector
bySwizzledSelector:(SEL)swizzledSelector;
@end
//
// NSObject+Swizzling.m
// Swizzling
//
// Created by peter.zhang on 2016/12/14.
// Copyright © 2016年 Peter. All rights reserved.
//
#import "NSObject+Swizzling.h"
@implementation NSObject (Swizzling)
+ (void)methodSwizzlingWithOriginalSelector:(SEL)originalSelector bySwizzledSelector:(SEL)swizzledSelector{
Class class = [self class];
//原有方法
Method originalMethod = class_getInstanceMethod(class, originalSelector);
//替换原有方法的新方法
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
//先尝试給源SEL添加IMP,这里是为了避免源SEL没有实现IMP的情况
BOOL didAddMethod = class_addMethod(class,originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {//添加成功:说明源SEL没有实现IMP,将源SEL的IMP替换到交换SEL的IMP
class_replaceMethod(class,swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {//添加失败:说明源SEL已经有IMP,直接将两个SEL的IMP交换即可
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
@end
-------------------------------以上是对Swizzling方法封装类别--------------------------------
runtime有很多用途:改变ViewController的生命周期、app热更新、改变系统方法调度(解决获取索引、添加、删除元素越界崩溃问题)等。今天主要说数组或者字典的越界crash问题。
啥都不是了,你把Swizzling方法封装类别添加到工程中:
以可变数组为例子:
//
// NSMutableArray+Security.h
// Swizzling
//
// Created by peter.zhang on 2016/12/14.
// Copyright © 2016年 Peter. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSMutableArray (Security)
@end
//
// NSMutableArray+Security.m
// Swizzling
//
// Created by peter.zhang on 2016/12/14.
// Copyright © 2016年 Peter. All rights reserved.
//
#import "NSMutableArray+Security.h"
#import "NSObject+Swizzling.h"
@implementation NSMutableArray (Security)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(removeObject:) bySwizzledSelector:@selector(safeRemoveObject:) ];
[objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(addObject:) bySwizzledSelector:@selector(safeAddObject:)];
[objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(removeObjectAtIndex:) bySwizzledSelector:@selector(safeRemoveObjectAtIndex:)];
[objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(insertObject:atIndex:) bySwizzledSelector:@selector(safeInsertObject:atIndex:)];
[objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(objectAtIndex:) bySwizzledSelector:@selector(safeObjectAtIndex:)];
});
}
- (void)safeAddObject:(id)obj {
if (obj == nil) {
NSLog(@"%s can add nil object into NSMutableArray", __FUNCTION__);
} else {
[self safeAddObject:obj];
}
}
- (void)safeRemoveObject:(id)obj {
if (obj == nil) {
NSLog(@"%s call -removeObject:, but argument obj is nil", __FUNCTION__);
return;
}
[self safeRemoveObject:obj];
}
- (void)safeInsertObject:(id)anObject atIndex:(NSUInteger)index {
if (anObject == nil) {
NSLog(@"%s can't insert nil into NSMutableArray", __FUNCTION__);
} else if (index > self.count) {
NSLog(@"%s index is invalid", __FUNCTION__);
} else {
[self safeInsertObject:anObject atIndex:index];
}
}
- (id)safeObjectAtIndex:(NSUInteger)index {
if (self.count == 0) {
NSLog(@"%s can't get any object from an empty array", __FUNCTION__);
return nil;
}
if (index > self.count) {
NSLog(@"%s index out of bounds in array", __FUNCTION__);
return nil;
}
return [self safeObjectAtIndex:index];
}
- (void)safeRemoveObjectAtIndex:(NSUInteger)index {
if (self.count <= 0) {
NSLog(@"%s can't get any object from an empty array", __FUNCTION__);
return;
}
if (index >= self.count) {
NSLog(@"%s index out of bound", __FUNCTION__);
return;
}
[self safeRemoveObjectAtIndex:index];
}
@end
然后你在工程中用可变数组的增删改查都不会crash了。
Runtime 函数 Swizzling 改变OC方法的调度顺序的更多相关文章
- iOS runtime探究(三): 从runtime開始理解OC的属性property
你要知道的runtime都在这里 转载请注明出处 http://blog.csdn.net/u014205968/article/details/67639303 本文主要解说runtime相关知识, ...
- OC方法交换swizzle详细介绍——不再有盲点
原文链接:https://www.cnblogs.com/mddblog/p/11105450.html 如果对方法交换已经比较熟悉,可以跳过整体介绍,直接看常见问题部分 整体介绍 方法交换是runt ...
- ES5——函数,对象,方法,this
JS由表达式和语句组成 表达式:计算出一个值,但并不进行任何操作,不改变计算机运行状态 语句:包括 声明语句,赋值语句,控制结构 函数,对象,方法,this 数组和对象:是两个非常重要的数据类型 函数 ...
- [iOS Hybrid实践:UIWebView中Html中用JS调用OC方法,OC执行JS代码]
原理: 1.JS调用OC 每次webview执行跳转时都会被iOS给拦截,执行下面函数获得系统允许. 因此可以根据跳转信息转给系统,执行相应功能,比如打开相册等. // 网页中的每一个请求都会被触发 ...
- 利用Objective-C运行时hook函数的三种方法
版权声明:转载请注明出处:http://blog.csdn.net/hursing 方法一,hook已有公开头文件的类: 首先写一个Utility函数: #import <objc/runtim ...
- OC方法和文件编译
OC方法和文件编译 一.OC方法 (一)对象方法 (1)对象方法以-开头如 -(void)xx; (2)对象方法只能又对象来调用 (3)对象方法中可以访问当前对象的成员变量 (4)调用格式 [对象 ...
- PHP使用feof()函数读文件的方法
这篇文章主要介绍了PHP使用feof()函数读文件的方法,以实例形式对比了正确与错误的用法,阐明了feof()函数的使用技巧,需要的朋友可以参考下 本文实例讲述了PHP使用feof()函数读文件的方法 ...
- [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法
js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...
- Objective-C Runtime 运行时之三:方法与消息
基础数据类型 SEL SEL又叫选择器,是表示一个方法的selector的指针,其定义如下: typedef struct objc_selector *SEL; objc_selector结构体的详 ...
随机推荐
- 简单的map转换成Bean的工具
简单的map转换成Bean的工具 package com.sd.microMsg.util; import java.lang.reflect.Field; import java.lang.refl ...
- js为链接绑定点击事件并且附带return false;来阻止跳转
<!DOCTYPE HTML> <html> <head> <meta charset="gb2312" /> <title& ...
- Python/Numpy大数据编程经验
Python/Numpy大数据编程经验 1.边处理边保存数据,不要处理完了一次性保存.不然程序跑了几小时甚至几天后挂了,就啥也没有了.即使部分结果不能实用,也可以分析程序流程的问题或者数据的特点. ...
- HDU2167+状态压缩DP
状态压缩dp 详见代码 /* 状态压缩dp dp[ i ][ j ]:第i行j状态的最大和 dp[i][j] = max( dp[i-1][k]+sum[i][j] ); 题意:给定一个N*N的方格, ...
- SPRING IN ACTION 第4版笔记-第六章RENDERING WEB VIEWS-004- <s:url>、<s:escapeBody>标签
一.<s:url> <s:url>可以直接生成一个url或url变量,它会在href的基础上加上应用context 1. <a href="<s:url ...
- Android 发送HTTP GET POST 请求以及通过 MultipartEntityBuilder 上传文件
折腾了好几天的 HTTP 终于搞定了,经测试正常,不过是初步用例测试用的,因为后面还要修改先把当前版本保存在博客里吧. 其中POST因为涉及多段上传需要导入两个包文件,我用的是最新的 httpmine ...
- maven 的 oracle的Missing artifact com.oracle:******:jar:11.2.0.2.0
解决方法: 下载ojdbc6对应版本号的包.把下载的包放到: 然后dos命令: -Dpackaging=jar -Dfile=ojdbc6.jar 主:必须确保已经配置了你maven,如何配置mave ...
- S3C2410 实验三——块拷贝、字拷贝(寄存器的理解)
因为笔记做在 evernote 上,博客上就不再重新敲了. http://www.evernote.com/shard/s307/sh/5bd591a1-dbbd-4457-812a-17c08c22 ...
- WinForm实现简单的拖拽功能(C#)(2)
首先创建一个winform应用程序,添加listbox1与listbox2,拖拽listbox1的项到listbox2上去. 具体代码如下 namespace OLE拖拽{ public partia ...
- phpcms 2008 /preview.php SQL注入漏洞
漏洞版本: phpcms 2008 漏洞描述: phpcms2008 是一款基于 PHP+Mysql 架构的网站内容管理系统,也是一个开源的 PHP 开发平台. phpcms 2008的preview ...