[![CI Status](http://img.shields.io/travis/ValiantCat/XXShield.svg?style=flat)](https://travis-ci.org/ValiantCat/XXShield) [![Version](https://img.shields.io/cocoapods/v/XXShield.svg?style=flat)](http://cocoapods.org/pods/XXShield) [![License](https://img.shields.io/cocoapods/l/XXShield.svg?style=flat)](http://cocoapods.org/pods/XXShield) [![Platform](https://img.shields.io/cocoapods/p/XXShield.svg?style=flat)](http://cocoapods.org/pods/XXShield)
<!--markdown-->
<!-- toc -->
# 前言
正在运行的 APP 突然 Crash,是一件令人不爽的事,会流失用户,影响公司发展,所以 APP 运行时拥有防 Crash 功能能有效降低 Crash 率,提升 APP 稳定性。但是有时候 APP Crash 是应有的表现,我们不让 APPCrash 可能会导致别的逻辑错误,不过我们可以抓取到应用当前的堆栈信息并上传至相关的服务器,分析并修复这些 BUG。
所以本文介绍的 XXShield 库有两个重要的功能:
1. 防止Crash
2. 捕获异常状态下的崩溃信息
类似的相关技术分析也有 [网易iOS App运行时Crash自动防护实践](https://mp.weixin.qq.com/s?__biz=MzA3ODg4MDk0Ng==&mid=2651113088&idx=1&sn=10b28d7fbcdf0def1a47113e5505728d&chksm=844c6f5db33be64b57fc9b4013cdbb7122d7791f3bbca9b4b29e80cce295eb341b03a9fc0f2f&mpshare=1&scene=23&srcid=0207njmGS2HWHI0BZmpjNKhv%23rd)
# 目前已经实现的功能
1. Unrecognized Selector Crash
2. KVO Crash
3. Container Crash
4. NSNotification Crash
5. NSNull Crash
6. NSTimer Crash
7. 野指针 Crash
----
# 1 Unrecoginzed Selector Crash
## 出现原因
由于 Objective-C 是动态语言,所有的消息发送都会放在运行时去解析,有时候我们把一个信息传递给了错误的类型,就会导致这个错误。
## 解决办法
Objective-C 在出现无法解析的方法时有三部曲来进行消息转发。
详见[Objective-C Runtime 运行时之三:方法与消息](https://southpeak.github.io/2014/11/03/objective-c-runtime-3/)
1. 动态方法解析
2. 备用接收者
3. 完整转发
1 一般适用与 Dynamic 修饰的 Property
2 一般适用与将方法转发至其他对象
3 一般适用与消息可以转发多个对象,可以实现类似多继承或者转发中心的概念。
这里选择的是方案二,因为三里面用到了 NSInvocation 对象,此对象性能开销较大,而且这种异常如果出现必然频次较高。最适合将消息转发到一个备用者对象上。
这里新建一个智能转发类。此对象将在其他对象无法解析数据时,返回一个 0 来防止 Crash。返回 0 是因为这个通用的智能转发类做的操作接近向 nil 发送一个消息。
代码如下
```Objc
#import <objc/runtime.h>
/**
default Implement
@param target trarget
@param cmd cmd
@param ... other param
@return default Implement is zero
*/
int smartFunction(id target, SEL cmd, ...) {
return 0;
}
static BOOL __addMethod(Class clazz, SEL sel) {
NSString *selName = NSStringFromSelector(sel);
NSMutableString *tmpString = [[NSMutableString alloc] initWithFormat:@"%@", selName];
int count = (int)[tmpString replaceOccurrencesOfString:@":"
withString:@"_"
options:NSCaseInsensitiveSearch
range:NSMakeRange(0, selName.length)];
NSMutableString *val = [[NSMutableString alloc] initWithString:@"i@:"];
for (int i = 0; i < count; i++) {
[val appendString:@"@"];
}
const char *funcTypeEncoding = [val UTF8String];
return class_addMethod(clazz, sel, (IMP)smartFunction, funcTypeEncoding);
}
@implementation XXShieldStubObject
+ (XXShieldStubObject *)shareInstance {
static XXShieldStubObject *singleton;
if (!singleton) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [XXShieldStubObject new];
});
}
return singleton;
}
- (BOOL)addFunc:(SEL)sel {
return __addMethod([XXShieldStubObject class], sel);
}
+ (BOOL)addClassFunc:(SEL)sel {
Class metaClass = objc_getMetaClass(class_getName([XXShieldStubObject class]));
return __addMethod(metaClass, sel);
}
@end
```
我们这里需要 Hook NSObject的 `- (id)forwardingTargetForSelector:(SEL)aSelector` 方法启动消息转发。
很多人不知道的是如果想要转发类方法,只需要实现一个同名的类方法即可,虽然在头文件中此方法并未声明。
```Objc
XXStaticHookClass(NSObject, ProtectFW, id, @selector(forwardingTargetForSelector:), (SEL)aSelector) {
// 1 如果是NSSNumber 和NSString没找到就是类型不对 切换下类型就好了
if ([self isKindOfClass:[NSNumber class]] && [NSString instancesRespondToSelector:aSelector]) {
NSNumber *number = (NSNumber *)self;
NSString *str = [number stringValue];
return str;
} else if ([self isKindOfClass:[NSString class]] && [NSNumber instancesRespondToSelector:aSelector]) {
NSString *str = (NSString *)self;
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
NSNumber *number = [formatter numberFromString:str];
return number;
}
BOOL aBool = [self respondsToSelector:aSelector];
NSMethodSignature *signatrue = [self methodSignatureForSelector:aSelector];
if (aBool || signatrue) {
return XXHookOrgin(aSelector);
} else {
XXShieldStubObject *stub = [XXShieldStubObject shareInstance];
[stub addFunc:aSelector];
NSString *reason = [NSString stringWithFormat:@"*****Warning***** logic error.target is %@ method is %@, reason : method forword to SmartFunction Object default implement like send message to nil.",
[self class], NSStringFromSelector(aSelector)];
[XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeUnrecognizedSelector];
return stub;
}
}
XXStaticHookEnd
```
这里汇报了 Crash 信息,出现消息转发一般是一个 logic 错误,为必须修复的Bug,上报尤为重要。
----
# 2 KVO Crash
## 出现原因
KVOCrash总结下来有以下2大类。
1. 不匹配的移除和添加关系。
2. 观察者和被观察者释放的时候没有及时断开观察者关系。
## 解决办法
> 尼古拉斯赵四说过 :![赵四](https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1487130185&di=41675c8578b69177ee9172e488c803f7&imgtype=jpg&er=1&src=http%3A%2F%2Fimgsrc.baidu.com%2Fforum%2Fw%3D580%2Fsign%3Dd1ac5ff09e25bc312b5d01906edf8de7%2Fcedafc039245d6882866389aa3c27d1ed21b244b.jpg)
> 对比到程序世界就是,程序世界没有什么难以解决的问题都是不可以通过抽象层次来解决的,如果有,那就两层。
> 纵观程序的架构设计,计算机网络协议分层设计,操作系统内核设计等等都是如此。
问题1 : 不成对的添加观察者和移除观察者会导致 Crash,以往我们使用 KVO,观察者和被观察者都是直接交互的。这里的设计方案是我们找一个 Proxy 用来做转发, 真正的观察者是 Proxy,被观察者出现了通知信息,由 Proxy 做分发。所以 Proxy 里面要保存一个数据结构 {keypath : [observer1, observer2,...]} 。
```Objc
@interface XXKVOProxy : NSObject {
__unsafe_unretained NSObject *_observed;
}
/**
{keypath : [ob1,ob2](NSHashTable)}
*/
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSHashTable<NSObject *> *> *kvoInfoMap;
@end
```
我们需要 Hook NSObject的 KVO 相关方法。
```Objc
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
```
1. 在添加观察者时
![addObserver](http://ompeszjl2.bkt.clouddn.com/iOS-APP-%E8%
没有合适的资源?快使用搜索试试~ 我知道了~
XXShield:这是一个可以避免由Objective-C编写的iOS项目崩溃的库
共81个文件
m:31个
h:20个
plist:3个
需积分: 9 2 下载量 166 浏览量
2021-04-14
06:52:23
上传
评论
收藏 77KB ZIP 举报
温馨提示
前言 正在运行的 APP 突然 Crash,是一件令人不爽的事,会流失用户,影响公司发展,所以 APP 运行时拥有防 Crash 功能能有效降低 Crash 率,提升 APP 稳定性。但是有时候 APP Crash 是应有的表现,我们不让 APPCrash 可能会导致别的逻辑错误,不过我们可以抓取到应用当前的堆栈信息并上传至相关的服务器,分析并修复这些 BUG。 所以本文介绍的 XXShield 库有两个重要的功能: 防止Crash 捕获异常状态下的崩溃信息 类似的相关技术分析也有 目前已经实现的功能 Unrecognized Selector Crash KVO Crash Container Crash NSNotification Crash NSNull Crash NSTimer Crash 野指针 Crash 1 Unrecoginzed Selector Crash 出现原因
资源详情
资源评论
资源推荐
收起资源包目录
XXShield-master.zip (81个子文件)
XXShield-master
.gitignore 1KB
Example
XXShield.xcodeproj
xcshareddata
xcschemes
XXShield_Tests.xcscheme 2KB
XXShield-Example.xcscheme 4KB
project.xcworkspace
contents.xcworkspacedata 153B
project.pbxproj 38KB
XXShield.xcworkspace
xcshareddata
IDEWorkspaceChecks.plist 238B
contents.xcworkspacedata 226B
XXShield
XXTimerViewController.m 1KB
main.m 329B
Student.m 244B
XXAppDelegate.m 2KB
XXViewController.m 499B
XXTimerViewController.h 224B
XXViewController.h 208B
Person.m 564B
XXKVOViewController.h 220B
XXKVOViewController.m 3KB
XXAppDelegate.h 270B
Images.xcassets
AppIcon.appiconset
Contents.json 1KB
File.swift 154B
XXShield-Prefix.pch 321B
XXShield-Info.plist 2KB
Student.h 257B
XXShield_Example-Bridging-Header.h 104B
Person.h 259B
Base.lproj
LaunchScreen.storyboard 2KB
Main.storyboard 7KB
en.lproj
InfoPlist.strings 45B
Tests
XXShield_Tests-Bridging-Header.h 104B
SDKSetup.m 755B
Tests-Prefix.pch 193B
NullSpec.m 1KB
DanglingPointerSpec.m 634B
Tests-Info.plist 674B
UnrecognizedSelectorSpec.m 966B
NotificationSpec.m 862B
XXNotificationObserver.m 328B
ContainerSpec.m 6KB
XXSpec.swift 168B
en.lproj
InfoPlist.strings 45B
XXTestNotification.m 645B
XXNotificationObserver.h 265B
Podfile.lock 470B
Podfile 199B
.travis.yml 607B
_Pods.xcodeproj 27B
LICENSE 12KB
README.md 20KB
Changelog.md 488B
XXShield.podspec 2KB
XXShield
Assets
.gitkeep 0B
XXShield.modulemap 97B
Classes
SmartKit
XXShieldStubObject.h 356B
XXShieldStubObject.m 2KB
NSObject+Forward.m 3KB
Notification
NSNotificationCenter+Shield.m 2KB
FoundationContainer
NSMutableArray+Shield.m 3KB
NSCache+Shield.m 1KB
NSArray+Shield.m 2KB
NSMutableDictionary+Shield.m 2KB
NSDictionary+Shield.m 1KB
XXShieldSDK.m 3KB
KVO
NSObject+KVOShield.m 6KB
XXShield.h 330B
.gitkeep 0B
NSNull
NSNull+Shield.m 574B
NSTimer
NSTimer+Shield.m 2KB
template
NSArrayWithObjects.h 584B
NSArrayObjectAtIndex.h 729B
Swizzle
XXMetamacros.h 30KB
XXShieldSwizzling.m 3KB
XXShieldSwizzling.h 18KB
Record
XXRecord.m 919B
XXRecord.h 499B
DanglingPointerShield
XXDanglingPointStub.m 1000B
XXDanglingPointStub.h 258B
NSObject+DanglingPointer.h 260B
NSObject+DanglingPointer.mm 1KB
XXDanglingPonterService.m 617B
XXDanglingPonterService.h 325B
XXShieldSDK.h 2KB
共 81 条
- 1
MaDaniel
- 粉丝: 53
- 资源: 4573
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
评论0