版本記錄
版本號 | 時(shí)間 |
---|---|
V1.0 | 2017.07.27 |
前言
OC是運(yùn)行時(shí)的語言,底層就是運(yùn)行時(shí),可以說runtime是OC的底層,很多事情也都可以用運(yùn)行時(shí)解決,下面就講述一下運(yùn)行時(shí)runtime的知識(shí)以及它的妙用。感興趣的可以看上面幾篇。
1. 運(yùn)行時(shí)runtime深度解析(一)—— API
2. 運(yùn)行時(shí)runtime深度解析(二)—— Method Swizzling在頁面統(tǒng)計(jì)上的應(yīng)用
數(shù)組越界
前面一篇文章講的是方法互換在頁面統(tǒng)計(jì)上的應(yīng)用,本篇文章主要介紹下一點(diǎn),那就是運(yùn)行時(shí)Method Swizzling
在數(shù)組越界上的應(yīng)用。數(shù)組越界是最常見和經(jīng)典的錯(cuò)誤,蘋果在這方面沒有警告,凡是數(shù)組越界直接就crash
,下面我們就用運(yùn)行時(shí)寫一個(gè)數(shù)組越界的警告。
我們對NSArray
、NSMutableArray
、NSDictionary
、NSMutableDictionary
等類進(jìn)行Method Swizzling
,實(shí)現(xiàn)方式還是按照上面的例子來做。但是會(huì)發(fā)現(xiàn)Method Swizzling根本就不起作用,這是因?yàn)?code>Method Swizzling對NSArray
這些的類簇是不起作用的。因?yàn)檫@些類簇類,其實(shí)是一種抽象工廠的設(shè)計(jì)模式。抽象工廠內(nèi)部有很多其它繼承自當(dāng)前類的子類,抽象工廠類會(huì)根據(jù)不同情況,創(chuàng)建不同的抽象對象來進(jìn)行使用。例如我們調(diào)用NSArray的objectAtIndex:
方法,這個(gè)類會(huì)在方法內(nèi)部判斷,內(nèi)部創(chuàng)建不同抽象類進(jìn)行操作。
所以也就是我們對NSArray類進(jìn)行操作其實(shí)只是對父類進(jìn)行了操作,在NSArray內(nèi)部會(huì)創(chuàng)建其他子類來執(zhí)行操作,真正執(zhí)行操作的并不是NSArray自身,所以我們應(yīng)該對其“真身”
進(jìn)行操作。
代碼實(shí)現(xiàn)
下面就直接看代碼,看一下Method Swizzling
在數(shù)組越界上的應(yīng)用。
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
下面看輸出結(jié)果
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
可見,由于數(shù)組越界,正常的拋出了我們定義的異常,以及系統(tǒng)給的崩潰日志。這里大家要注意NSArray
的真身是__NSArray
,對于這種工廠模式,其實(shí)其他類型的也都有真身,包括我們熟知的NSString
、NSDIctionary
等等。
下面我們打印一下幾個(gè)有代表性的類的真身。
類 | 真身 |
---|---|
NSArray | __NSArrayI |
NSMutableArray | __NSArrayM |
NSDictionary | __NSDictionaryI |
NSMutableDictionary | __NSDictionaryM |
最后介紹一個(gè)很有用的框架。
jrswizzle - Method Swizzling封裝
參考文獻(xiàn)
1. Method Swizzling
2. Objective-C Runtime 運(yùn)行時(shí)之四:Method Swizzling
3. iOS黑魔法-Method Swizzling
后記
未完,待續(xù)~~~