先来看一下OC中的数组、字典和字符串的真正类名
首先创建一个NSObject的分类
#import <Foundation/Foundation.h>
@interface NSObject (MSCategory)
// 此处className可以传字符串也可以传Class对象
OBJC_EXPORT void exchangeMethod(id _Nonnull className, SEL _Nonnull oldSelector, SEL _Nonnull newSelector)OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
OBJC_EXPORT void exchangeMethodStr(id _Nonnull className, NSString * _Nonnull oldSelector, NSString * _Nonnull newSelector)OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
@end
#import "NSObject+MSCategory.h"
#import <objc/runtime.h>
@implementation NSObject (MSCategory)
OBJC_EXPORT void exchangeMethod(id _Nonnull className, SEL _Nonnull oldSelector, SEL _Nonnull newSelector)OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0) {
Class cls = NSClassFromString([NSString stringWithFormat:@"%@", className]);
if (![cls isKindOfClass:[NSObject class]]) {
#if DEBUG
[[NSAssertionHandler currentHandler] handleFailureInFunction:@"exchangeMethod" file:@"NSObject+MSCategory.m" lineNumber:16 description:@"系统找不到这个类"];
#endif
return;
}
method_exchangeImplementations(class_getInstanceMethod(cls, oldSelector), class_getInstanceMethod(cls, newSelector));
}
OBJC_EXPORT void exchangeMethodStr(id _Nonnull className, NSString * _Nonnull oldSelector, NSString * _Nonnull newSelector)OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0) {
exchangeMethod(className, NSSelectorFromString(oldSelector), NSSelectorFromString(newSelector));
}
@end
然后创建一个NSArray的分类
#import "NSArray+MSCategory.h"
#import "NSObject+MSCategory.h"
@implementation NSArray (MSCategory)
+ (void)load {
exchangeMethod(@"__NSArray0", @selector(objectAtIndex:), @selector(ms0_objectAtIndex:));
exchangeMethod(@"__NSSingleObjectArrayI", @selector(objectAtIndex:), @selector(ms1_objectAtIndex:));
exchangeMethod(@"__NSArrayI", @selector(objectAtIndex:), @selector(msn_objectAtIndex:));
exchangeMethod(@"__NSArrayM", @selector(objectAtIndex:), @selector(ms_objectAtIndex:));
}
- (id)ms0_objectAtIndex:(NSUInteger)index {
#if DEBUG
NSAssert(NO, @"数组越界了");
#endif
// 可以在这里将相关信息传到服务器,来分析报错信息
return nil;
}
- (id)ms1_objectAtIndex:(NSUInteger)index {
if (index != 0) {
#if DEBUG
NSAssert(NO, @"数组越界了");
#endif
// 可以在这里将相关信息传到服务器,来分析报错信息
return nil;
}
return [self ms1_objectAtIndex:0];
}
- (id)msn_objectAtIndex:(NSUInteger)index {
if (index >= self.count) {
#if DEBUG
NSAssert(NO, @"数组越界了");
#endif
// 可以在这里将相关信息传到服务器,来分析报错信息
return nil;
}
return [self msn_objectAtIndex:index];
}
- (id)ms_objectAtIndex:(NSUInteger)index {
if (index >= self.count) {
#if DEBUG
NSAssert(NO, @"数组越界了");
#endif
// 可以在这里将相关信息传到服务器,来分析报错信息
return nil;
}
return [self ms_objectAtIndex:index];
}
@end
然后创建一个NSString的分类
注:由于所有的字符串类调用substringToIndex:
、substringFromIndex:
和substringWithRange:
三个方法时候都会进到__NSCFString
中,所以只需要hook __NSCFString
类即可
#import "NSString+MSCategory.h"
#import "NSObject+MSCategory.h"
@implementation NSString (MSCategory)
+ (void)load {
// NSString
exchangeMethod(@"__NSCFString", @selector(substringToIndex:), @selector(ms_substringToIndex:));
exchangeMethod(@"__NSCFString", @selector(substringFromIndex:), @selector(ms_substringFromIndex:));
exchangeMethod(@"__NSCFString", @selector(substringWithRange:), @selector(ms_substringWithRange:));
// NSMutableString
exchangeMethod(@"__NSCFString", @selector(replaceCharactersInRange:withString:), @selector(ms_replaceCharactersInRange:withString:));
exchangeMethod(@"__NSCFString", @selector(insertString:atIndex:), @selector(ms_insertString:atIndex:));
exchangeMethod(@"__NSCFString", @selector(deleteCharactersInRange:), @selector(ms_deleteCharactersInRange:));
}
#pragma mark - hook NSString 方法
- (NSString *)ms_substringToIndex:(NSUInteger)to {
if (to > self.length) {
#if DEBUG
NSAssert(NO, @"字符串长度越界了");
#endif
return nil;
}
return [self ms_substringToIndex:to];
}
- (NSString *)ms_substringFromIndex:(NSUInteger)from {
if (from > self.length) {
#if DEBUG
NSAssert(NO, @"字符串长度越界了");
#endif
return nil;
}
return [self ms_substringFromIndex:from];
}
- (NSString *)ms_substringWithRange:(NSRange)range {
NSInteger location = [NSString stringWithFormat:@"%ld", range.location].integerValue;
if (location < 0 || (range.location + range.length) > self.length) {
#if DEBUG
NSAssert(NO, @"字符串长度越界了");
#endif
return nil;
}
return [self ms_substringWithRange:range];
}
#pragma mark - hook NSMutableString 方法
- (void)ms_replaceCharactersInRange:(NSRange)range withString:(NSString *)aString {
NSInteger location = [NSString stringWithFormat:@"%ld", range.location].integerValue;
if (location < 0 || (range.location + range.length) > self.length) {
#if DEBUG
NSAssert(NO, @"字符串长度越界了");
#endif
return;
}
[self ms_replaceCharactersInRange:range withString:aString];
}
- (void)ms_insertString:(NSString *)aString atIndex:(NSUInteger)loc {
if (loc > self.length) {
#if DEBUG
NSAssert(NO, @"字符串长度越界了");
#endif
return;
}
[self ms_insertString:aString atIndex:loc];
}
- (void)ms_deleteCharactersInRange:(NSRange)range {
NSInteger location = [NSString stringWithFormat:@"%ld", range.location].integerValue;
if (location < 0 || (range.location + range.length) > self.length) {
#if DEBUG
NSAssert(NO, @"字符串长度越界了");
#endif
return;
}
[self ms_deleteCharactersInRange:range];
}
@end