前几天查看后台bug统计数据,又发现了一个数组越界导致的崩溃问题,非常让人头疼,因为数组越界问题已经在业务层做了无数的判空处理,每次出现问题之后都会进行反复测试,不断优化,自以为这个版本是不可能出现问题的,然而...... 那么没有办法,想要彻底解决这个问题,就不能只是单纯在业务层做判空处理了,换句话说,不管数组越界问题是否还会存在,我们需要采取措施,保证线上版本不能出现崩溃。一直把这个问题拖到现在,也是考虑到代码的健壮性,数组越界问题毕竟是业务层导致的,如果能在业务层解决问题,就应该把代码留在业务层。
-
思路
思路其实比较简单,用我们熟悉的runtime的Method Swizzling去hook造成数组越界的方法(NSArray的objectAtIndex:和objectAtIndexedSubscript:方法)。这里说一下objectAtIndex:和objectAtIndexedSubscript:,我们大可以认为这两个方法是一样的,objectAtIndex:是我们需要知道和使用的方法,objectAtIndexedSubscript:是提供对obj-c下标支持的方法,也可以说是编译器使用的方法。那么我们可以新建两个方法去和这两个方法实现交换。下面列出NSArray的实现类(可以在控制台上打印出来):
-
__NSArrayI:不可变正常数组
-
__NSArray0:不可变空数组
-
__NSSingleObjectArrayI:不可变单元素数组
-
__NSArrayM:可变数组
思路非常简单清晰,接下去我们只要hook这四个实现类的相关方法就ok了。
-
实现
引入用于Method Swizzling的类文件(NSObject的分类)NSObject+XPYMethodSwizzling
// NSObject+XPYMethodSwizzling.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSObject (XPYMethodSwizzling)
+ (void)xpy_classMethodSwizzlingWithOriginSel:(SEL)originSEL swizzlingSel:(SEL)swizzlingSEL;
+ (void)xpy_instanceMethodSwizzlingWithOriginSel:(SEL)originSEL swizzlingSel:(SEL)swizzlingSEL;
@end
NS_ASSUME_NONNULL_END
// NSObject+XPYMethodSwizzling.