1. 程式人生 > >用Method Swizzling來避免陣列越界與可變字典value值為nil造成的程式崩潰

用Method Swizzling來避免陣列越界與可變字典value值為nil造成的程式崩潰

程式崩潰對於app來說是最致命的bug,而陣列越界便是其中最重要的原因之一。我們可以應用Method Swizzling知識來避免這一問題。

1.我們來建立一個類別,繼承於NSArray:



2.然後在.m檔案中匯入 objc/runtime.h標頭檔案

#import "NSArray+EM.h"
#import <objc/runtime.h>

@implementation NSArray (EM)
+(void)load {
    Method fromMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
    Method toMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(em_objectAtIndex:));
    method_exchangeImplementations(fromMethod, toMethod);
}

- (void)em_objectAtIndex:(NSUInteger)index {
    if (self.count - 1 < index) {
        @try {
            return[self em_objectAtIndex:index];
        }
        @catch (NSException *exception) {
            NSLog(@"-------- %s Crash Because Method %s -------\n",class_getName(self.class),__func__);
            NSLog(@"%@", [exception callStackSymbols]);
        }
        @finally {
            
        }
    }else {
        return [self em_objectAtIndex:index];
    }
}
@end

細心的人會發現,下面這個方法會導致遞迴,其實不會的。

這實際上是運用了方法替換,當我們在其他類中 呼叫NSArray的objectAtIndex方法時,會被自動替換為執行em_objectAtIndex:方法,而當我們呼叫em_objectAtIndex:方法時,會被替換為執行objectAtIndex:方法。

我們也可以用這種方法同樣解決NSMutableArray。

#import "NSMutableArray+EM.h"
#import <objc/runtime.h>
@implementation NSMutableArray (EM)
+(void)load {
    Method fromMethod = class_getInstanceMethod(objc_getClass("__NSArrayM"), @selector(objectAtIndex:));
    Method toMethod = class_getInstanceMethod(objc_getClass("__NSArrayM"), @selector(em_objectAtIndex:));
    method_exchangeImplementations(fromMethod, toMethod);
    
}

- (void)em_objectAtIndex:(NSUInteger)index {
    if (self.count - 1 < index) {
        @try {
            return[self em_objectAtIndex:index];
        }
        @catch (NSException *exception) {
            NSLog(@"-------- %s Crash Because Method %s -------\n",class_getName(self.class),__func__);
            NSLog(@"%@", [exception callStackSymbols]);
        }
        @finally {
            
        }
    }else {
        return [self em_objectAtIndex:index];
    }
}

@end

還可以判斷NSMutableDictionary 的value值是否為nil

#import "NSMutableDictionary+EM.h"
#import <objc/runtime.h>
@implementation NSMutableDictionary (EM)
+ (void)load {
    
    Method fromMethod = class_getInstanceMethod(objc_getClass("__NSDictionaryM"), @selector(setObject:forKey:));
    Method toMethod = class_getInstanceMethod(objc_getClass("__NSDictionaryM"), @selector(em_setObject:forKey:));
    method_exchangeImplementations(fromMethod, toMethod);
}

- (void)em_setObject:(id)emObject forKey:(NSString *)key {
    if (emObject == nil) {
        @try {
            [self em_setObject:emObject forKey:key];
        }
        @catch (NSException *exception) {
            NSLog(@"---------- %s Crash Because Method %s  ----------\n", class_getName(self.class), __func__);
            NSLog(@"%@", [exception callStackSymbols]);
            emObject = [NSString stringWithFormat:@""];
            [self em_setObject:emObject forKey:key];
        }
        @finally {}
    }else {
        [self em_setObject:emObject forKey:key];
    }
}
@end


下面我們列舉一些常用的類簇的“真身”: