runtime解決iOS開發中數組越界致使crash問題

1、什麼是數組越界

數組越界,不少新手容易中招的bug,數組

NSArray *ar = @[@"123", @(456), @"haha"];
nsstring *str = ar[3];//數組取值越界了
複製代碼

這個看起來很簡單,可是實際開發應用中,各類數據變量在程序運行過程當中,是常常不斷變化的,很容易被忽視,一不當心就越界致使crash。咱們先不論爲何越界,這是另外一個議題,至少最基本的咱們要先保證程序不會奔潰是吧。服務器

2、數組越界最簡單的一種解決方案,固然就是取值的時候限制下index的值,好比:

NSArray *moduleTexts = @[@"資訊系統", @"數字資源", @"震例系統", @"百科系統"];
int index = self.moduleCode.intValue-1;//moduleCode是服務器返回值,NSString類型
index = MAX(MIN(index, 3), 0);
lb_moduleType.text = moduleTexts[index];
複製代碼

3、在數組的分類中使用runtime機制來交換方法

基本思路是:當數組越界時返回nil,沒有越界時返回本來的index.這樣就能達到防止程序崩潰的問題.ui

一、建立NSObject的Category分類

此分類文件就一個方法,該方法用runtime的機制來替換系統方法。spa

NSObject+ExchangeMethod.h文件:code

#import <Foundation/Foundation.h>
@interface NSObject (ExchangeMethod)

/** * 對系統方法進行替換(交換實例方法) * * @param systemSelector 被替換的方法 * @param changedSelector 實際使用的方法 * @param error 替換過程當中出現的錯誤消息 * * @return 是否替換成功 */
+ (BOOL)exchangedSystemSelector:(SEL)systemSelector withSelector:(SEL)changedSelector error:(NSError *)error;

@end
複製代碼

建立.m文件NSObject+ExchangeMethod.m:ip

#import "NSObject+ExchangeMethod.h"
#import <objc/runtime.h>

@implementation NSObject (ExchangeMethod)
+ (BOOL)exchangedSystemSelector:(SEL)systemSelector withSelector:(SEL)changedSelector error:(NSError *)error{

    Method systemMethod = class_getInstanceMethod(self, systemSelector);
    if (!systemMethod) {
        return NO;
    }

    Method changedMethod = class_getInstanceMethod(self, changedSelector);
    if (!changedMethod) {
        return NO;
    }

    if (class_addMethod([self class], systemSelector, method_getImplementation(changedMethod), method_getTypeEncoding(changedMethod))) {
        class_replaceMethod([self class], changedSelector, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
    }else{
        method_exchangeImplementations(systemMethod, changedMethod);
    }

    return YES;
}
@end
複製代碼

二、建立NSArray的分類,

NSArray+Overflow.h文件:資源

#import <objc/runtime.h>
#import <Foundation/Foundation.h>

@interface NSArray (Overflow)

@end

複製代碼

NSArray+Overflow.m文件:開發

#import "NSArray+Overflow.h"
#import "NSObject+ExchangeMethod.h"
#import <objc/runtime.h>

@implementation NSArray (Overflow)

+(void)load{
    [super load];
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [objc_getClass("__NSArrayI") exchangedSystemSelector:@selector(objectAtIndex:) withSelector:@selector(hd_objectAtIndex:) error:nil];
        [objc_getClass("__NSArrayI") exchangedSystemSelector:@selector(objectAtIndexedSubscript:) withSelector:@selector(hd_objectAtIndexedSubscript:) error:nil];
    });
}
- (id)sxy_objectAtIndexedSubscript:(NSUInteger)index{
    if (index < self.count) {
        return [self hd_objectAtIndexedSubscript:index];
    }else{
        NSLog(@"Error:數組越界了,index = %ld, count = %ld", index, self.count);
        return nil;
    }
}

- (id)sxy_objectAtIndex:(NSUInteger)index{
    if (index < self.count) {
        return [self hd_objectAtIndex:index];
    }else{
        NSLog(@"Error:數組越界了,index = %ld, count = %ld", index, self.count);
        return nil;
    }
}
@end
複製代碼

三、建立NSMutabeArray的分類,

NSMutableArray+Overflow.h文件:get

#import <Foundation/Foundation.h>

@interface NSMutableArray (Overflow)

@end
複製代碼

NSMutableArray+Overflow.m文件:string

#import "NSMutableArray+Overflow.h"
#import "NSObject+ExchangeMethod.h"

@implementation NSMutableArray (Overflow)
+(void)load{
    [super load];
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [objc_getClass("__NSArrayM") exchangedSystemSelector:@selector(objectAtIndex:) withSelector:@selector(hd_objectAtIndex:) error:nil];
        [objc_getClass("__NSArrayM") exchangedSystemSelector:@selector(objectAtIndexedSubscript:) withSelector:@selector(hd_objectAtIndexedSubscript:) error:nil];
    });
}
- (id)hd_objectAtIndexedSubscript:(NSUInteger)idx{
    if (idx < self.count) {
        return [self hd_objectAtIndexedSubscript:idx];
    }else{
        NSLog(@"Error:數組越界了,index = %ld, count = %ld", idx, self.count);
        return nil;
    }
}

- (id)hd_objectAtIndex:(NSUInteger)index{
    if (index < self.count) {
        return [self hd_objectAtIndex:index];
    }else{
        NSLog(@"Error:數組越界了,index = %ld, count = %ld", index, self.count);
        return nil;
    }
}
@end
複製代碼

四、最後在項目工程下XXXX-Prefix.pch文件裏添加NSArray和NSMutableAray的分類頭文件就好了:

#ifdef __OBJC__
#define MAS_SHORTHAND

#import "NSArray+overflow.h"
#import "NSMutableArray+Overflow.h"

#endif
複製代碼
相關文章
相關標籤/搜索