iOS - Crash防護(hù)主要有以下幾個方面
1,子線程訪問UI:通過runtime攔截幾個UI方法,并且監(jiān)聽當(dāng)前線程,如果不是主線程則強(qiáng)行轉(zhuǎn)回主線程,并且可以進(jìn)行上報
[objc_getClass("UIView") swizzleMethod:@selector(setNeedsLayout) swizzledSelector:@selector(replace_setNeedsLayout)];
[objc_getClass("UIView") swizzleMethod:@selector(setNeedsDisplay) swizzledSelector:@selector(replace_setNeedsDisplay)];
-(void)replace_setNeedsLayout{
if ([NSThread isMainThread]) {
return [self replace_setNeedsLayout];
}else{
dispatch_async(dispatch_get_main_queue(), ^{
return [self replace_setNeedsLayout];
});
}
}
-(void)replace_setNeedsDisplay{
if ([NSThread isMainThread]) {
return [self replace_setNeedsDisplay];
}else{
dispatch_async(dispatch_get_main_queue(), ^{
return [self replace_setNeedsDisplay];
});
}
}
2,調(diào)用不存在函數(shù)(hook:forwardingTargetForSelector,把方法轉(zhuǎn)到一個自定義對象,并在改對象添加該不存在方法
[objc_getClass("NSObject") swizzleMethod:@selector(forwardingTargetForSelector:) swizzledSelector:@selector(replace_forwardingTargetForSelector:)];
- (id)replace_forwardingTargetForSelector:(SEL)aSelector
{
NSMethodSignature *signature = [self methodSignatureForSelector:aSelector];
if ([self respondsToSelector:aSelector] || signature) {
return [self replace_forwardingTargetForSelector:aSelector];
}
return [NSObject createFakeForwardTargetObject:self selector:aSelector];
}
+ (id)createFakeForwardTargetObject:(id)aTarget selector:(SEL)aSelector
{
if ([[NSString string] respondsToSelector:aSelector]) {
NSString *szTarget = nil;
if ([aTarget isKindOfClass:[NSNumber class]]) {
szTarget = [NSString stringWithFormat:@"%@", aTarget];
}
if (szTarget) {
return szTarget;
}
}
FakeForwardTargetObject *fakeTaget = [[FakeForwardTargetObject alloc] initWithSelector:aSelector];
return fakeTaget;
}
3,KVO重復(fù)添加監(jiān)聽回調(diào)去重功能,KVO重復(fù)remove導(dǎo)致crash問題.方案:runtime攔截,添加移除Api,通過KVC拿到私有api,進(jìn)行判斷去重
[objc_getClass("NSObject") swizzleMethod:@selector(removeObserver:forKeyPath:) swizzledSelector:@selector(removeDasen:forKeyPath:)];
[objc_getClass("NSObject") swizzleMethod:@selector(addObserver:forKeyPath:options:context:) swizzledSelector:@selector(addDasen:forKeyPath:options:context:)];
// 交換后的方法
- (void)removeDasen:(NSObject *)observer forKeyPath:(NSString *)keyPath
{
if ([self observerKeyPath:keyPath observer:observer]) {
[self removeDasen:observer forKeyPath:keyPath];
}
}
// 交換后的方法
- (void)addDasen:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
objc_setAssociatedObject(self, "addObserverFlag", @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (![self observerKeyPath:keyPath observer:observer]) {
[self addDasen:observer forKeyPath:keyPath options:options context:context];
}
}
// 進(jìn)行檢索獲取Key
- (BOOL)observerKeyPath:(NSString *)key observer:(id )observer
{
id info = self.observationInfo;
NSArray *array = [info valueForKey:@"_observances"];
for (id objc in array) {
id Properties = [objc valueForKeyPath:@"_property"];
id newObserver = [objc valueForKeyPath:@"_observer"];
NSString *keyPath = [Properties valueForKeyPath:@"_keyPath"];
if ([key isEqualToString:keyPath] && [newObserver isEqual:observer]) {
return YES;
}
}
return NO;
}
4,iOS9.0系統(tǒng)以上監(jiān)聽對象dealloc,沒有移除通知導(dǎo)致崩潰問題.方案:通過runtimehook添加和deallocApi,進(jìn)行移除操作
[objc_getClass("NSNotificationCenter") swizzleMethod:@selector(addObserver:selector:name:object:) swizzledSelector:@selector(ghl_addObserver:selector:name:object:)];
[objc_getClass("NSObject") swizzleMethod:NSSelectorFromString(@"dealloc") swizzledSelector:@selector(replaceDealloc)];
- (void)replaceDealloc
{
NSString *addObserver = objc_getAssociatedObject(self, "addObserverFlag");
if ([addObserver boolValue]) {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
[self replaceDealloc];
}
- (void)ghl_addObserver:(id)observer selector:(SEL)aSelector name:(NSNotificationName)aName object:(id)anObject {
[self ghl_addObserver:observer selector:aSelector name:aName object:anObject];
objc_setAssociatedObject(observer, "addObserverFlag", @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
5,容器類問題:oc數(shù)組越界,字符串操作越界,UITableView和UICollectionView操作數(shù)據(jù)越界問題.具體查看demo.這里可以提一句,類似NSArray這種類是一種類簇,類似一種多態(tài)的操作,內(nèi)部根據(jù)某些情況會調(diào)用具體的類
demo:https://github.com/riceForChina/SafeTool.git
參考文章:https://neyoufan.github.io/2017/01/13/ios/BayMax_HTSafetyGuard/#kvo