美文网首页
runtime解决各种崩溃问题

runtime解决各种崩溃问题

作者: _moses | 来源:发表于2018-06-09 13:53 被阅读60次

先来看一下OC中的数组、字典和字符串的真正类名


类簇.png 类簇.png

首先创建一个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

相关文章

网友评论

      本文标题:runtime解决各种崩溃问题

      本文链接:https://www.haomeiwen.com/subject/mteteftx.html