运行时runtime深度解析(三)—— Method Swizzling在数组越界上的应用

版本记录

版本号 时间
V1.0 2017.07.27

前言

OC是运行时的语言,底层就是运行时,可以说runtime是OC的底层,很多事情也都可以用运行时解决,下面就讲述一下运行时runtime的知识以及它的妙用。感兴趣的可以看上面几篇。
1. 运行时runtime深度解析(一)—— API
2. 运行时runtime深度解析(二)—— Method Swizzling在页面统计上的应用

数组越界

前面一篇文章讲的是方法互换在页面统计上的应用,本篇文章主要介绍下一点,那就是运行时Method Swizzling在数组越界上的应用。数组越界是最常见和经典的错误,苹果在这方面没有警告,凡是数组越界直接就crash,下面我们就用运行时写一个数组越界的警告。

我们对NSArrayNSMutableArrayNSDictionaryNSMutableDictionary等类进行Method Swizzling,实现方式还是按照上面的例子来做。但是会发现Method Swizzling根本就不起作用,这是因为Method SwizzlingNSArray这些的类簇是不起作用的。因为这些类簇类,其实是一种抽象工厂的设计模式。抽象工厂内部有很多其它继承自当前类的子类,抽象工厂类会根据不同情况,创建不同的抽象对象来进行使用。例如我们调用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,对于这种工厂模式,其实其他类型的也都有真身,包括我们熟知的NSStringNSDIctionary等等。

下面我们打印一下几个有代表性的类的真身。

真身
NSArray __NSArrayI
NSMutableArray __NSArrayM
NSDictionary __NSDictionaryI
NSMutableDictionary __NSDictionaryM

最后介绍一个很有用的框架。
jrswizzle - Method Swizzling封装


参考文献

1. Method Swizzling
2. Objective-C Runtime 运行时之四:Method Swizzling
3. iOS黑魔法-Method Swizzling

后记

未完,待续~~~

范爷很美
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容