数组取数据组越界的问题我想每个程序🐒都遇到过无数次,我们这章不谈如何解决bug,不谈这样做
是否正确, 只谈如何通过runtime交换objectatidex方法导致crash的问题用于对runtime的
理解扩展(!!!注意,此方法并不会解决我们代码所产生的bug,仅仅会防止整个项目中数组的
越界crash问题,所以不要以为这样做了就可以不管bug了)
同上一章,我们需要建一个pch文件加到prefix headder中,在pch中我们包含这个category
众所周知try catch方法可以让编译器在遇到crash情况时抛出异常而不至于crash,那我们就通过
替换经常crash的objectatindex方法替换为try catch的lxzObjectAtIndex来实现
[demo传送门]https://github.com/joy-make/nsarrayBounds.git
category具体实现如下
#import "NSArray+LXZArray.h"
@implementation NSArray (LXZArray)
+(void)load{
[super load];
//定义方法a
Method fromMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
//定义方法b
Method toMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(lxzObjectAtIndex:));
//交换方法a和b
method_exchangeImplementations(fromMethod, toMethod);
}
- (id)lxzObjectAtIndex:(NSInteger)index{
if (self.count-1<index) {
@try
{
[self lxzObjectAtIndex:index];
}
@catch (NSException *exception)
{
NSLog(@"---------- %s Crash Because Method %s ----------\n", class_getName(self.class), __func__);
NSLog(@"ERROR:%@",[exception callStackSymbols]);
}@finally {//xxx}
return nil;
}
else{ return [self lxzObjectAtIndex:index];}
}
runtime 交换方法还有好多其他的用处,就不一一举例了
@end
测试一下
@interface ViewController (){
NSTimer *_timer;
}
@property (weak, nonatomic) IBOutlet UILabel *displayLabel;
@property (nonatomic,strong)NSArray *listArray;
@end
@implementation ViewController
static int repeats = 0;
- (void)viewDidLoad {
[super viewDidLoad];
__weak __typeof(&*self)weakSelf = self;
_timer = [NSTimer scheduledTimerWithTimeInterval:0.4 repeats:10 block:^(NSTimer * _Nonnull timer) {
weakSelf.displayLabel.text = repeats>=weakSelf.listArray.count-1?[NSString stringWithFormat:@"第%d个元素应该crash但没有crash",repeats++]:weakSelf.listArray[repeats++];
}];
_timer.fireDate = [NSDate distantFuture];
}
-(NSArray *)listArray{
return _listArray =_listArray?:@[@"0",@"1",@"2"];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (IBAction)testListCrash:(id)sender {
repeats = 0;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
_timer.fireDate = [NSDate distantPast];
});
}