版本记录
版本号 | 时间 |
---|---|
V1.0 | 2017.07.27 |
前言
OC是运行时的语言,底层就是运行时,可以说runtime是OC的底层,很多事情也都可以用运行时解决,下面就讲述一下运行时runtime的知识以及它的妙用。感兴趣的可以看上面几篇。
1. 运行时runtime深度解析(一)—— API
2. 运行时runtime深度解析(二)—— Method Swizzling在页面统计上的应用
数组越界
前面一篇文章讲的是方法互换在页面统计上的应用,本篇文章主要介绍下一点,那就是运行时Method Swizzling
在数组越界上的应用。数组越界是最常见和经典的错误,苹果在这方面没有警告,凡是数组越界直接就crash
,下面我们就用运行时写一个数组越界的警告。
我们对NSArray
、NSMutableArray
、NSDictionary
、NSMutableDictionary
等类进行Method Swizzling
,实现方式还是按照上面的例子来做。但是会发现Method Swizzling根本就不起作用,这是因为Method Swizzling
对NSArray
这些的类簇是不起作用的。因为这些类簇类,其实是一种抽象工厂的设计模式。抽象工厂内部有很多其它继承自当前类的子类,抽象工厂类会根据不同情况,创建不同的抽象对象来进行使用。例如我们调用NSArray的objectAtIndex:
方法,这个类会在方法内部判断,内部创建不同抽象类进行操作。
所以也就是我们对NSArray类进行操作其实只是对父类进行了操作,在NSArray内部会创建其他子类来执行操作,真正执行操作的并不是NSArray自身,所以我们应该对其“真身”
进行操作。
代码实现
下面就直接看代码,看一下Method Swizzling
在数组越界上的应用。
1. NSArray+JJArrayCategory.h
#import <Foundation/Foundation.h>
@interface NSArray (JJArrayCategory)
@end
2.NSArray+JJArrayCategory.m
#import "NSArray+JJArrayCategory.h"
#import <objc/runtime.h>
@implementation NSArray (JJArrayCategory)
#pragma mark - Override Base Function
+ (void)load
{
[super load];
Method fromMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
Method toMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(JJ_objectAtIndex:));
method_exchangeImplementations(fromMethod, toMethod);
}
#pragma mark - Action && Notification
- (id)JJ_objectAtIndex:(NSInteger)index
{
if (self.count - 1 < index) {
@try {
return [self JJ_objectAtIndex:index];
}
@catch (NSException *exception) {
NSLog(@"---------- %s Crash Because Method %s ----------\n", class_getName(self.class), __func__);
NSLog(@"%@", [exception callStackSymbols]);
return nil;
}
@finally {
NSLog(@"nothing to do");
}
}
else {
return [self JJ_objectAtIndex:index];
}
}
@end
3. JJRuntimeVC.h
#import <UIKit/UIKit.h>
@interface JJRuntimeVC : UIViewController
@end
4.JJRuntimeVC.m
#import "JJRuntimeVC.h"
@interface JJRuntimeVC ()
@end
@implementation JJRuntimeVC
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor greenColor];
NSArray *arr = @[@(1), @(2), @(3), @(4)];
NSInteger value = [[arr objectAtIndex:4] integerValue];
}
@end
下面看输出结果
2017-07-27 19:48:57.121641+0800 JJOC[5768:1916584] ---------- __NSArrayI Crash Because Method -[NSArray(JJArrayCategory) JJ_objectAtIndex:] ----------
2017-07-27 19:48:57.132802+0800 JJOC[5768:1916584] (
0 CoreFoundation 0x0000000183d86ff0 <redacted> + 148
1 libobjc.A.dylib 0x00000001827e8538 objc_exception_throw + 56
2 CoreFoundation 0x0000000183c652c8 CFRunLoopRemoveTimer + 0
3 JJOC 0x000000010005bdb4 -[NSArray(JJArrayCategory) JJ_objectAtIndex:] + 88
4 JJOC 0x000000010005acf8 -[JJRuntimeVC viewDidLoad] + 556
5 UIKit 0x0000000189eb5f9c <redacted> + 1036
6 UIKit 0x0000000189f6e0c4 <redacted> + 72
7 UIKit 0x0000000189f6df9c <redacted> + 416
8 UIKit 0x0000000189f6d2cc <redacted> + 144
9 UIKit 0x0000000189f6cd00 <redacted> + 856
10 UIKit 0x0000000189f6c8b4 <redacted> + 64
11 UIKit 0x0000000189f6c818 <redacted> + 188
12 UIKit 0x0000000189eb3158 <redacted> + 1200
13 QuartzCore 0x00000001870a3274 <redacted> + 148
14 QuartzCore 0x0000000187097de8 <redacted> + 292
15 QuartzCore 0x0000000187097ca8 <redacted> + 32
16 QuartzCore 0x0000000187013360 <redacted> + 252
17 QuartzCore 0x000000018703a3c0 <redacted> + 504
18 QuartzCore 0x000000018703ae8c <redacted> + 120
19 CoreFoundation 0x0000000183d349a0 <redacted> + 32
20 CoreFoundation 0x0000000183d32628 <redacted> + 372
21 CoreFoundation 0x0000000183c62db4 CFRunLoopRunSpecific + 456
22 UIKit 0x0000000189f2045c <redacted> + 652
23 UIKit 0x0000000189f1b130 UIApplicationMain + 208
24 JJOC 0x000000010005c050 main + 124
25 libdyld.dylib 0x0000000182c7159c <redacted> + 4
)
2017-07-27 19:49:10.451961+0800 JJOC[5768:1916584] nothing to do
可见,由于数组越界,正常的抛出了我们定义的异常,以及系统给的崩溃日志。这里大家要注意NSArray
的真身是__NSArray
,对于这种工厂模式,其实其他类型的也都有真身,包括我们熟知的NSString
、NSDIctionary
等等。
下面我们打印一下几个有代表性的类的真身。
类 | 真身 |
---|---|
NSArray | __NSArrayI |
NSMutableArray | __NSArrayM |
NSDictionary | __NSDictionaryI |
NSMutableDictionary | __NSDictionaryM |
最后介绍一个很有用的框架。
jrswizzle - Method Swizzling封装
参考文献
1. Method Swizzling
2. Objective-C Runtime 运行时之四:Method Swizzling
3. iOS黑魔法-Method Swizzling
后记
未完,待续~~~