作了个升级检查,其中有一段代码直接下标访问的数组arr[0]
,我敢这样写,由于我用runtime
判断了数组越界。可是我如今发现没写全,我真是个大傻子😂,关键是我还写了测试代码。数组
先说不可变数组,它的实际实现类有三种:bash
__NSArrayI
多个元素__NSArray0
空数组__NSSingleObjectArrayI
单个元素这些实现类怎么获得,写个代码,断点看下就明白了 测试
类方法(class_getClassMethod
)和对象方法(class_getInstanceMethod
)均可以添加,这里以对象方法为例ui
NSObject+Swizzling.h
spa
+ (void)swizzlingInstanceMethodOrigSEL:(SEL)origSEL swizzleSEL:(SEL)swizzleSEL{
Method origMe = class_getInstanceMethod(self, origSEL);
Method swizzleMe = class_getInstanceMethod(self, swizzleSEL);
// 无论原方法存不存在,添加原方法看下结果, 这个只交换了一半(SEL和Method关联)
BOOL addOrigMe = class_addMethod(self, origSEL, method_getImplementation(swizzleMe), method_getTypeEncoding(swizzleMe));
// 添加原方法成功,说明原方法以前不存在
// 而后交换剩下的一半(SEL和Method关联)
if (addOrigMe) {
class_replaceMethod(self, swizzleSEL, method_getImplementation(origMe), method_getTypeEncoding(origMe));
}
// 添加原方法失败,说明原方法以前是存在的,就能够直接替换Method
else{
method_exchangeImplementations(origMe, swizzleMe);
}
}
复制代码
带上以前找到的实现类,就能够判断数组越界了code
#import "NSArray+Safe.h"
cdn
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class arrayI = objc_getClass("__NSArrayI");
Class arrayEmpty = objc_getClass("__NSArray0");
Class arraySingle = objc_getClass("__NSSingleObjectArrayI");
[arrayI swizzlingInstanceMethodOrigSEL:@selector(objectAtIndex:) swizzleSEL:@selector(sf_objectAtIndex:)];
[arrayI swizzlingInstanceMethodOrigSEL:@selector(objectAtIndexedSubscript:) swizzleSEL:@selector(sf_objectAtIndexedSubscript:)];
[arrayEmpty swizzlingInstanceMethodOrigSEL:@selector(objectAtIndex:) swizzleSEL:@selector(empty_objectAtIndex:)];
[arrayEmpty swizzlingInstanceMethodOrigSEL:@selector(objectAtIndexedSubscript:) swizzleSEL:@selector(empty_objectAtIndexedSubscript:)];
[arraySingle swizzlingInstanceMethodOrigSEL:@selector(objectAtIndex:) swizzleSEL:@selector(single_objectAtIndex:)];
[arraySingle swizzlingInstanceMethodOrigSEL:@selector(objectAtIndexedSubscript:) swizzleSEL:@selector(single_objectAtIndexedSubscript:)];
});
}
#pragma mark - __NSArrayI
- (id)sf_objectAtIndex:(NSUInteger)index{
if (index > self.count - 1) {
return nil;
}
return [self sf_objectAtIndex:index];
}
- (id)sf_objectAtIndexedSubscript:(NSUInteger)idx{
if (idx > self.count - 1) {
return nil;
}
return [self sf_objectAtIndexedSubscript:idx];
}
#pragma mark - __NSArray0
- (id)empty_objectAtIndex:(NSUInteger)index{
return nil;
}
- (id)empty_objectAtIndexedSubscript:(NSUInteger)idx{
return nil;
}
#pragma mark - __NSSingleObjectArrayI
- (id)single_objectAtIndex:(NSUInteger)index{
if (index > self.count - 1) {
return nil;
}
return [self single_objectAtIndex:index];
}
- (id)single_objectAtIndexedSubscript:(NSUInteger)idx{
if (idx > self.count - 1) {
return nil;
}
return [self single_objectAtIndexedSubscript:idx];
}
复制代码
若是不看前面,确实是死循环了,傻子才这样写😂。 可是前面交换了方法实现,以single_objectAtIndex
为例子,简化一下就是对象
__NSSingleObjectArrayI.single_objectAtIndex = __NSSingleObjectArrayI.objectAtIndex
复制代码
那么在single_objectAtIndex
方法内部再次调用single_objectAtIndex
,其实至关于调用了原来的方法objectAtIndex
blog
这里面,有重复代码,single_objectAtIndex
和sf_objectAtIndex
,难道不能合在一块儿吗?答案能忍!是真不能合在一块儿,缘由是,屡次交换后回到了原来的方法。以__NSSingleObjectArrayI
为例,若是写成这样ip
[arraySingle swizzlingInstanceMethodOrigSEL:@selector(objectAtIndex:) swizzleSEL:@selector(sf_objectAtIndex:)];
[arraySingle swizzlingInstanceMethodOrigSEL:@selector(objectAtIndexedSubscript:) swizzleSEL:@selector(sf_objectAtIndexedSubscript:)];
复制代码
由于__NSArrayI
交换了一轮了
简化下大概像这样,sf_objectAtIndex
目前的实现就是__NSArrayI.objectAtIndex
self.sf_objectAtIndex = __NSArrayI.objectAtIndex
复制代码
若是__NSSingleObjectArrayI
直接交换sf_objectAtIndex
,那么结果就是
__NSSingleObjectArrayI.objectAtIndex = __NSArrayI.objectAtIndex
复制代码
因此这个时候会崩溃,仍是老老实实的写每一个实现类的方法。
实现类就只有一个__NSArrayM
,实现同上