前言
很多时候,我们常常在使用数组的时候因为数组越界而导致程序崩溃。我们通过runtime
中的特殊手段,实现在运行时互换函数,达到偷天换日效果。
Method Swizzling
Method Swizzling是发生在运行时期的,主要是在运行时,将两个Method交换。达到偷天换日的效果,Method Swizzling是面向切面编程的一种技术。
OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2)
__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
#import <Foundation/Foundation.h>
/**
* 不可变数组防越界崩溃处理
*/
@interface NSArray (AXKArray)
@end
#import "NSArray+AXKArray.h"
#import <objc/runtime.h>
@implementation NSArray (AXKArray)
+ (void)load
{
[super load];
Method originalMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
Method userMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(axk_objectAtIndex:));
if (!class_addMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:), method_getImplementation(userMethod), method_getTypeEncoding(userMethod))) {
method_exchangeImplementations(originalMethod, userMethod);
}
}
- (id)axk_objectAtIndex:(NSUInteger)index
{
if (self.count - 1 < index) {
//异常处理,捕获异常并抛出异
@try {
return [self axk_objectAtIndex:index];
}
@catch (NSException *exception) {
NSLog(@"不可变数组越界访问异常--%s--Crash Because Method--%s---以上异常", class_getName(self.class), __func__);
NSLog(@"异常下标--%lu--", index);
NSLog(@"不可变数组越界访问异常日志%@", [exception callStackSymbols]);
return nil;
}
@finally {
}
} else {
return [self axk_objectAtIndex:index];
}
}
@end
类簇、替身、真身
在iOS中存在大量的有关类簇、真身、替身相关的类等
大家发现了吗,__NSArrayI才是NSArray真正的类,而NSMutableArray又不一样。我们可以通过runtime函数获取真正的类:
objc_getClass("__NSArrayI")
下面我们列举一些常用的类簇的“真身”:
类 | 真身 |
---|---|
NSArray | __NSArrayI |
NSMutableArray | __NSArrayM |
NSDictionary | __NSDictionaryI |
NSMutableDictionary | __NSDictionaryM |
Method Swizzling方法只能对替身的真身有效,对替身没有效果。