作了個升級檢查,其中有一段代碼直接下標訪問的數組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
,實現同上