├── iOS └── QYCrashProtector │ ├── QYCrashProtector.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── zhi.zhuang.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ ├── xcuserdata │ │ └── zhi.zhuang.xcuserdatad │ │ │ ├── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ │ │ └── xcschemes │ │ │ ├── xcschememanagement.plist │ │ │ ├── QYCrashProtector.xcscheme │ │ │ ├── eeeeee.xcscheme │ │ │ ├── iMessage.xcscheme │ │ │ ├── QYMessage.xcscheme │ │ │ └── 表情包测试.xcscheme │ └── project.pbxproj │ ├── QYCrashProtector │ ├── AClass.h │ ├── BClass.h │ ├── SwizzlingTest.h │ ├── AppDelegate.h │ ├── Test.h │ ├── main.m │ ├── AClass.m │ ├── KVOTest.h │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── CrashProtector │ │ ├── CPManager.h │ │ ├── CPManager.m │ │ ├── NSObject+CrashProtector.h │ │ └── NSObject+CrashProtector.m │ ├── BClass.m │ ├── Info.plist │ ├── Test.m │ ├── SwizzlingTest.m │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── AppDelegate.m │ └── KVOTest.m │ └── QYMessage │ ├── MessagesViewController.h │ ├── MessagesViewController.m │ └── Base.lproj │ └── MainInterface.storyboard └── README.md /iOS/QYCrashProtector/QYCrashProtector.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector.xcodeproj/xcuserdata/zhi.zhuang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/AClass.h: -------------------------------------------------------------------------------- 1 | // 2 | // AClass.h 3 | // QYCrashProtector 4 | // 5 | // Created by Zhi Zhuang on 2017/10/18. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AClass : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector.xcodeproj/project.xcworkspace/xcuserdata/zhi.zhuang.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiyer/QYCrashProtector/HEAD/iOS/QYCrashProtector/QYCrashProtector.xcodeproj/project.xcworkspace/xcuserdata/zhi.zhuang.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYMessage/MessagesViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MessagesViewController.h 3 | // QYMessage 4 | // 5 | // Created by Zhi Zhuang on 2017/10/24. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MessagesViewController : MSMessagesAppViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/BClass.h: -------------------------------------------------------------------------------- 1 | // 2 | // BClass.h 3 | // QYCrashProtector 4 | // 5 | // Created by Zhi Zhuang on 2017/10/24. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BClass : NSObject 12 | @property (nonatomic,strong) NSString * kvoName; 13 | -(void)doAddObserver; 14 | @end 15 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/SwizzlingTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // SwizzlingTest.h 3 | // QYCrashProtector 4 | // 5 | // Created by Zhi Zhuang on 2017/9/13. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SwizzlingTest : NSObject 12 | 13 | -(void)doFuncA; 14 | -(void)doFuncB; 15 | 16 | -(void)swizzling; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // QYCrashProtector 4 | // 5 | // Created by Zhi Zhuang on 2017/9/4. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/Test.h: -------------------------------------------------------------------------------- 1 | // 2 | // Test.h 3 | // QYCrashProtector 4 | // 5 | // Created by Zhi Zhuang on 2017/9/13. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface Test : NSObject 12 | 13 | @property(nonatomic,copy) NSString* kvo_name; 14 | 15 | 16 | -(void)doTimerTest; 17 | -(void)doNotificationTest; 18 | 19 | -(void)testKVO; 20 | @end 21 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // QYCrashProtector 4 | // 5 | // Created by Zhi Zhuang on 2017/9/4. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/AClass.m: -------------------------------------------------------------------------------- 1 | // 2 | // AClass.m 3 | // QYCrashProtector 4 | // 5 | // Created by Zhi Zhuang on 2017/10/18. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import "AClass.h" 10 | 11 | @implementation AClass 12 | { 13 | NSString * name; 14 | } 15 | 16 | -(void)dealloc 17 | { 18 | // __weak __typeof(self) wkself = self; 19 | // NSLog(@"DD:%@",wkself); 20 | name = @"ddddd"; 21 | NSLog(@"xxxx:%@",name); 22 | NSLog(@"DD:%@",self); 23 | id __weak obj1 = self; 24 | NSLog(@"DD:%@",obj1); 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/KVOTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // KVOTest.h 3 | // QYCrashProtector 4 | // 5 | // Created by Zhi Zhuang on 2017/10/20. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSObject (Test) 12 | +(void)doSw; 13 | @end 14 | 15 | @interface KVOTest : NSObject 16 | @property (nonatomic,strong) NSString * kvoName; 17 | 18 | -(BOOL)swizzlingInstance:(Class)clz orginalMethod:(SEL)originalSelector replaceMethod:(SEL)replaceSelector; 19 | -(void)doAddObserver; 20 | -(void)doSwizzling; 21 | @end 22 | 23 | 24 | @interface KVODelegate : NSObject 25 | 26 | -(void)setSelf:(NSObject*) weak; 27 | 28 | @end 29 | 30 | @interface KVOObj : NSObject 31 | 32 | @property (nonatomic,strong) NSString * kvoNames; 33 | 34 | @end 35 | 36 | 37 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/CrashProtector/CPManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // CPManager.h 3 | // QYCrashProtector 4 | // 5 | // Created by Zhi Zhuang on 2017/9/4. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger, CrashProtectorStyle) { 12 | CrashProtectorNone = 0, 13 | CrashProtectorAll , 14 | CrashProtectorUnrecognizedSelector, 15 | CrashProtectorKVO , 16 | CrashProtectorNotification , 17 | CrashProtectorTimer , 18 | CrashProtectorContainer , 19 | CrashProtectorString , 20 | }; 21 | 22 | @interface CPConfiguration : NSObject 23 | 24 | @property(nonatomic,assign) Boolean openLog; 25 | @property(nonatomic,assign) Boolean isDebug; 26 | @property(nonatomic,assign) CrashProtectorStyle style; 27 | 28 | -(instancetype)initDefault; 29 | 30 | @end 31 | 32 | 33 | @interface CPManager : NSObject 34 | 35 | +(instancetype)instance; 36 | 37 | -(void)initWithConfig:(CPConfiguration *) config; 38 | 39 | -(void)start; 40 | -(void)stop; 41 | 42 | -(void)setLog:(Boolean) isOpen; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/BClass.m: -------------------------------------------------------------------------------- 1 | // 2 | // BClass.m 3 | // QYCrashProtector 4 | // 5 | // Created by Zhi Zhuang on 2017/10/24. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import "BClass.h" 10 | 11 | @implementation BClass 12 | 13 | -(void)doAddObserver 14 | { 15 | NSLog(@"addObserver"); 16 | [self addObserver:self forKeyPath:@"kvoName" options:NSKeyValueObservingOptionNew context:nil]; 17 | [self addObserver:self forKeyPath:@"kvoName" options:NSKeyValueObservingOptionNew context:nil]; 18 | [self addObserver:self forKeyPath:@"kvoName" options:NSKeyValueObservingOptionNew context:nil]; 19 | self.kvoName = @"ddsdsddssds"; 20 | } 21 | 22 | - (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary *)change context:(nullable void *)context{ 23 | NSLog(@"observeValueForKeyPath:%@",change); 24 | } 25 | 26 | -(void)dealloc 27 | { 28 | // [self removeObserver:self forKeyPath:@"kvoName"]; 29 | // [self removeObserver:self forKeyPath:@"kvoName"]; 30 | NSLog(@"Bclass is dealloc"); 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/CrashProtector/CPManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // CPManager.m 3 | // QYCrashProtector 4 | // 5 | // Created by Zhi Zhuang on 2017/9/4. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import "CPManager.h" 10 | 11 | @implementation CPConfiguration 12 | 13 | -(instancetype)initDefault 14 | { 15 | if(self = [super init]){ 16 | self.isDebug = NO; 17 | self.openLog = NO; 18 | self.style = CrashProtectorAll; 19 | } 20 | return self; 21 | } 22 | @end 23 | 24 | 25 | 26 | @implementation CPManager 27 | { 28 | CPConfiguration* configs; 29 | } 30 | 31 | +(instancetype)instance 32 | { 33 | static CPManager* _instance; 34 | static dispatch_once_t onceToken; 35 | dispatch_once(&onceToken, ^{ 36 | if (nil == _instance) { 37 | _instance = [[CPManager alloc] init]; 38 | } 39 | }); 40 | return _instance; 41 | } 42 | 43 | -(void)initWithConfig:(CPConfiguration*) config 44 | { 45 | configs = config?:[[CPConfiguration alloc] initDefault]; 46 | } 47 | 48 | -(void)start 49 | { 50 | 51 | } 52 | 53 | -(void)stop 54 | { 55 | 56 | } 57 | 58 | -(void)setLog:(Boolean) isOpen 59 | { 60 | 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QYCrashProtector 2 | iOS crash protect 3 | 关于为什么实现都在NSObject+CrashProtector.m,而不是搞些分类实现? 4 | 5 | 个人认为,功能类 目的是封装好给别人用的,而不是让大家都可以去看其实现、学习。放在一个类里实现,好处就是精简;坏处就是出问题查起来麻烦,看实现也不是很清晰。 6 | 7 | ## 如何使用? 8 | 9 | 1.导入 NSObject+CrashProtector.h NSObject+CrashProtector.m ; 10 | 11 | 2.NSObject+CrashProtector.h 文件里面 #define CP_OPEN YES 为开启保护, NO为关闭保护 ; 12 | 13 | 3.建议测试期间 CP_OPEN 设为NO,这样能暴露问题 解决问题,待上线时候设为YES,减少APP的崩溃率 ;或者CP_OPEN 设为NO 调用[NSObject openCP];在需要的时候动态开启; 14 | 15 | 4.log部分楼主还在抽时间完善,公司大大小小会议有点多,写代码时间很少,抱歉。 16 | 17 | 5.有同学 说xcode9.2 运行 函数越界的 会不起作用;我在xcode 8.3 、xcode 9.0 测试没有问题,xcode 9.2 还没有试过; 18 | 19 | 注意:最好通过[NSObject openCP] 在app启动后调用,如果通过CP_OPEN 宏控制,在+load 时候开始保护,有可能会干扰系统底层自身的一些调用。 20 | 21 | ## 作用? 22 | 23 | 1. unrecognized selector crash 24 | 25 | 2. NSTimer crash 26 | 27 | 3. Container crash(数组越界,插nil等) 28 | 29 | 4. NSString crash (字符串操作的crash) 30 | 31 | 5. NSNotification crash 32 | 33 | 6. KVO crash(譬如某一属性的多次侦听,或是忘记removeObserver 或是多次removeObserver导致的crash) 34 | 35 | 7. KVC crash 36 | 37 | ## 了解更多? 38 | 39 | 因为公司产品比较多,用了一些bug统计,发现某些产品闪退率还不低,为了降低闪退率,于是有了这个crash保护小样。在写的过程也搜索了一下同类的案例,发现一个写得特别好的,且比较全面的产品,即网易开发的 大白(Baymax),也做了比较详细的介绍:http://mp.weixin.qq.com/s/GFt7uqrKw7m3R3KrV43zIQ 。美中不足的是,他们没有开源。楼主实现的功能,该文档都有讲到,思路也基本一致,大家可以学习使用。我也将抽更多时间完善它。 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/Test.m: -------------------------------------------------------------------------------- 1 | // 2 | // Test.m 3 | // QYCrashProtector 4 | // 5 | // Created by Zhi Zhuang on 2017/9/13. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import "Test.h" 10 | #import "NSObject+CrashProtector.h" 11 | 12 | @implementation Test 13 | { 14 | NSTimer * timer; 15 | } 16 | 17 | -(void)doTimerTest 18 | { 19 | timer = [NSTimer scheduledTimerWithTimeInterval:2.0f target:self selector:@selector(testLog) userInfo:nil repeats:YES]; 20 | } 21 | 22 | -(void)doNotificationTest 23 | { 24 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationCallback) name:@"notificationCallback_test" object:nil]; 25 | 26 | self.kvo_name = @"ccccccc"; 27 | } 28 | 29 | -(void)notificationCallback 30 | { 31 | NSLog(@"%s",__func__); 32 | } 33 | 34 | -(void)testLog 35 | { 36 | NSLog(@"Test:%s",__func__); 37 | } 38 | 39 | -(void)dealloc 40 | { 41 | NSLog(@"Test class is dealloc!"); 42 | [self removeObserver:self forKeyPath:@"kvo_name"]; 43 | } 44 | 45 | 46 | -(void)testKVO 47 | { 48 | [self addObserver:self forKeyPath:@"kvo_name" options:NSKeyValueObservingOptionNew context:nil]; 49 | self.kvo_name = @"66666"; 50 | } 51 | 52 | - (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary *)change context:(nullable void *)context{ 53 | NSLog(@"observeValueForKeyPath:%@",keyPath); 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector.xcodeproj/xcuserdata/zhi.zhuang.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | QYCrashProtector.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | QYMessage.xcscheme 13 | 14 | orderHint 15 | 1 16 | 17 | eeeeee.xcscheme 18 | 19 | orderHint 20 | 2 21 | 22 | iMessage.xcscheme 23 | 24 | orderHint 25 | 4 26 | 27 | 表情包测试.xcscheme 28 | 29 | orderHint 30 | 3 31 | 32 | 33 | SuppressBuildableAutocreation 34 | 35 | 0101450A1FC82A23001F666D 36 | 37 | primary 38 | 39 | 40 | 0157BF171F9F275C0056FB64 41 | 42 | primary 43 | 44 | 45 | 016CFBE71F9F325B00448773 46 | 47 | primary 48 | 49 | 50 | 016CFBF41F9F340100448773 51 | 52 | primary 53 | 54 | 55 | 01F021E71F5D4B0200BDB21E 56 | 57 | primary 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/SwizzlingTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // SwizzlingTest.m 3 | // QYCrashProtector 4 | // 5 | // Created by Zhi Zhuang on 2017/9/13. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import "SwizzlingTest.h" 10 | #import 11 | 12 | @implementation SwizzlingTest 13 | 14 | 15 | -(void)doFuncA 16 | { 17 | NSLog(@"%s",__func__); 18 | 19 | } 20 | 21 | -(void)doFuncB 22 | { 23 | NSLog(@"%s",__func__); 24 | } 25 | 26 | -(void)swizzling 27 | { 28 | Class class = [self class]; 29 | 30 | SEL originalSelector = @selector(doFuncA); 31 | SEL swizzledSelector = @selector(doFuncB); 32 | Method originalMethod = class_getInstanceMethod(class, originalSelector); 33 | Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 34 | if (!originalMethod || !swizzledMethod) { 35 | return; 36 | } 37 | 38 | IMP originalIMP = method_getImplementation(originalMethod); 39 | IMP swizzledIMP = method_getImplementation(swizzledMethod); 40 | const char *originalType = method_getTypeEncoding(originalMethod); 41 | const char *swizzledType = method_getTypeEncoding(swizzledMethod); 42 | 43 | // 这儿的先后顺序是有讲究的,如果先执行后一句,那么在执行完瞬间方法被调用容易引发死循环 44 | class_replaceMethod(class,swizzledSelector,originalIMP,originalType); 45 | 46 | IMP originalIMP1 = method_getImplementation(originalMethod); 47 | IMP swizzledIMP1 = method_getImplementation(swizzledMethod); 48 | 49 | class_replaceMethod(class,originalSelector,swizzledIMP,swizzledType); 50 | 51 | IMP originalIMP2 = method_getImplementation(originalMethod); 52 | IMP swizzledIMP2 = method_getImplementation(swizzledMethod); 53 | NSLog(@"dddddd"); 54 | } 55 | @end 56 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYMessage/MessagesViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MessagesViewController.m 3 | // QYMessage 4 | // 5 | // Created by Zhi Zhuang on 2017/10/24. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import "MessagesViewController.h" 10 | 11 | 12 | @interface MessagesViewController () 13 | 14 | @end 15 | 16 | @implementation MessagesViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view. 21 | } 22 | 23 | - (void)didReceiveMemoryWarning { 24 | [super didReceiveMemoryWarning]; 25 | // Dispose of any resources that can be recreated. 26 | } 27 | 28 | #pragma mark - Conversation Handling 29 | 30 | -(void)didBecomeActiveWithConversation:(MSConversation *)conversation { 31 | // Called when the extension is about to move from the inactive to active state. 32 | // This will happen when the extension is about to present UI. 33 | 34 | // Use this method to configure the extension and restore previously stored state. 35 | } 36 | 37 | -(void)willResignActiveWithConversation:(MSConversation *)conversation { 38 | // Called when the extension is about to move from the active to inactive state. 39 | // This will happen when the user dissmises the extension, changes to a different 40 | // conversation or quits Messages. 41 | 42 | // Use this method to release shared resources, save user data, invalidate timers, 43 | // and store enough state information to restore your extension to its current state 44 | // in case it is terminated later. 45 | } 46 | 47 | -(void)didReceiveMessage:(MSMessage *)message conversation:(MSConversation *)conversation { 48 | // Called when a message arrives that was generated by another instance of this 49 | // extension on a remote device. 50 | 51 | // Use this method to trigger UI updates in response to the message. 52 | } 53 | 54 | -(void)didStartSendingMessage:(MSMessage *)message conversation:(MSConversation *)conversation { 55 | // Called when the user taps the send button. 56 | } 57 | 58 | -(void)didCancelSendingMessage:(MSMessage *)message conversation:(MSConversation *)conversation { 59 | // Called when the user deletes the message without sending it. 60 | 61 | // Use this to clean up state related to the deleted message. 62 | } 63 | 64 | -(void)willTransitionToPresentationStyle:(MSMessagesAppPresentationStyle)presentationStyle { 65 | // Called before the extension transitions to a new presentation style. 66 | 67 | // Use this method to prepare for the change in presentation style. 68 | } 69 | 70 | -(void)didTransitionToPresentationStyle:(MSMessagesAppPresentationStyle)presentationStyle { 71 | // Called after the extension transitions to a new presentation style. 72 | 73 | // Use this method to finalize any behaviors associated with the change in presentation style. 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // QYCrashProtector 4 | // 5 | // Created by Zhi Zhuang on 2017/9/4. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "Test.h" 11 | //#import "NSObject+CrashProtector.h" 12 | #import "SwizzlingTest.h" 13 | 14 | #import "NSObject+CrashProtector.h" 15 | 16 | #import "AClass.h" 17 | #import "KVOTest.h" 18 | #import "BClass.h" 19 | 20 | @interface AppDelegate () 21 | 22 | @end 23 | 24 | @implementation AppDelegate 25 | 26 | 27 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 28 | 29 | 30 | // BClass * bclass = [BClass new]; 31 | // [bclass doAddObserver]; 32 | // return YES; 33 | 34 | // KVOTest * kvo = [KVOTest new]; 35 | // [kvo doSwizzling]; 36 | // [kvo doAddObserver]; 37 | // 38 | // return YES; 39 | [NSObject openCP]; 40 | 41 | int a[10]; 42 | 43 | Test * test = [[Test alloc] init]; 44 | [test performSelector:@selector(haha) withObject:nil]; 45 | 46 | 47 | NSString * tt = nil; 48 | NSDictionary * dic = @{@"sss":@"cccc",@"name":tt}; 49 | 50 | for (id obj in dic) { 51 | NSLog(@"dic:%@",obj); 52 | } 53 | NSMutableDictionary * dic2 = [[NSMutableDictionary alloc] init]; 54 | [dic2 setObject:tt forKey:@"cccc"]; 55 | [dic2 setObject:@"xxxx" forKey:@"key1"]; 56 | [dic2 setObject:@"xxxx" forKey:@"key2"]; 57 | 58 | [dic objectForKey:tt]; 59 | NSLog(@"dddd"); 60 | 61 | NSArray * arr = @[@"111",@"222",tt,@"ddd"]; 62 | NSArray * arr2 = @[@[@"aa",@"bb"],@[@"dd",tt]]; 63 | NSArray *arr3 = [NSArray arrayWithObjects:@"dsd",tt,@"sdsd", nil]; 64 | // arr[3]; 65 | 66 | NSMutableArray * arr4 = [NSMutableArray array]; 67 | [arr4 addObject:tt]; 68 | [arr4 insertObject:tt atIndex:0]; 69 | [arr4 insertObject:@"ccc" atIndex:0]; 70 | [arr4 objectAtIndex:2]; 71 | 72 | NSString * str = [[NSString alloc] initWithString:nil]; 73 | [@"dddd" hasSuffix:nil]; 74 | 75 | NSString * str2 = [NSMutableString stringWithString:nil]; 76 | 77 | [test doTimerTest]; 78 | 79 | [test doNotificationTest]; 80 | 81 | [test testKVO]; 82 | NSLog(@"xxxxxx"); 83 | 84 | return YES; 85 | } 86 | 87 | 88 | - (void)applicationWillResignActive:(UIApplication *)application { 89 | } 90 | 91 | 92 | - (void)applicationDidEnterBackground:(UIApplication *)application { 93 | } 94 | 95 | 96 | - (void)applicationWillEnterForeground:(UIApplication *)application { 97 | } 98 | 99 | 100 | - (void)applicationDidBecomeActive:(UIApplication *)application { 101 | } 102 | 103 | 104 | - (void)applicationWillTerminate:(UIApplication *)application { 105 | } 106 | 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYMessage/Base.lproj/MainInterface.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector.xcodeproj/xcuserdata/zhi.zhuang.xcuserdatad/xcschemes/QYCrashProtector.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/CrashProtector/NSObject+CrashProtector.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+CrashProtector.h 3 | // QYCrashProtector 4 | // 5 | // Created by Zhi Zhuang on 2017/9/4. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define CP_OPEN NO 12 | 13 | @interface CrashProxy : NSObject 14 | 15 | @property (nonatomic,copy) NSString * _Nullable crashMsg; 16 | 17 | - (id _Nullable)getCrashMsg; 18 | 19 | @end 20 | 21 | //----------------------------------------------------------------------------------------------------------------------------- 22 | // NSObject 23 | @interface NSObject (CrashProtector) 24 | 25 | + (void)openCP; 26 | + (BOOL)swizzlingInstanceMethod:(SEL _Nullable )originalSelector replaceMethod:(SEL _Nullable )replaceSelector; 27 | 28 | @end 29 | 30 | //----------------------------------------------------------------------------------------------------------------------------- 31 | // NSDictionary 32 | @interface NSDictionary (CrashProtector) 33 | 34 | @end 35 | 36 | //----------------------------------------------------------------------------------------------------------------------------- 37 | // NSMutableDictionary 38 | @interface NSMutableDictionary (CrashProtector) 39 | 40 | @end 41 | 42 | //----------------------------------------------------------------------------------------------------------------------------- 43 | // NSArray 44 | @interface NSArray (CrashProtector) 45 | 46 | @end 47 | 48 | //----------------------------------------------------------------------------------------------------------------------------- 49 | // NSMutableArray 50 | @interface NSMutableArray (CrashProtector) 51 | 52 | @end 53 | 54 | //----------------------------------------------------------------------------------------------------------------------------- 55 | // NSString 56 | @interface NSString (CrashProtector) 57 | 58 | @end 59 | 60 | //----------------------------------------------------------------------------------------------------------------------------- 61 | // NSMutableString 62 | @interface NSMutableString (CrashProtector) 63 | 64 | @end 65 | 66 | //----------------------------------------------------------------------------------------------------------------------------- 67 | //CPWeakProxy 68 | //拿了YY大神的YYWeakProxy,感谢YY大神 链接 :https://github.com/ibireme/YYKit/blob/master/YYKit/Utility/YYWeakProxy.h 69 | @interface CPWeakProxy : NSProxy 70 | 71 | /** 72 | The proxy target. 73 | */ 74 | @property (nullable, nonatomic, weak, readonly) id target; 75 | 76 | /** 77 | Creates a new weak proxy for target. 78 | 79 | @param target Target object. 80 | 81 | @return A new proxy object. 82 | */ 83 | - (instancetype _Nullable )initWithTarget:(id _Nullable )target; 84 | 85 | /** 86 | Creates a new weak proxy for target. 87 | 88 | @param target Target object. 89 | 90 | @return A new proxy object. 91 | */ 92 | + (instancetype _Nullable )proxyWithTarget:(id _Nullable )target; 93 | 94 | @end 95 | 96 | //----------------------------------------------------------------------------------------------------------------------------- 97 | // NSTimer 98 | @interface NSTimer (CrashProtector) 99 | 100 | @end 101 | 102 | //----------------------------------------------------------------------------------------------------------------------------- 103 | //KVOProxy 104 | @class CPKVOInfo; 105 | @interface KVOProxy : NSObject 106 | 107 | -(BOOL)addKVOinfo:(id _Nullable )object info:(CPKVOInfo *_Nullable)info; 108 | -(void)removeKVOinfo:(id _Nullable )object keyPath:(NSString *_Nullable)keyPath block:(void(^_Nullable)()) block; 109 | -(void)removeAllObserve; 110 | @end 111 | 112 | typedef void (^CPKVONotificationBlock)(id _Nullable observer, id _Nullable object, NSDictionary * _Nullable change); 113 | 114 | //----------------------------------------------------------------------------------------------------------------------------- 115 | //CPKVOInfo 116 | @interface CPKVOInfo : NSObject 117 | 118 | - (instancetype _Nullable )initWithKeyPath:(NSString *_Nullable)keyPath options:(NSKeyValueObservingOptions)options context:(void *_Nullable)context; 119 | 120 | @end 121 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector.xcodeproj/xcuserdata/zhi.zhuang.xcuserdatad/xcschemes/eeeeee.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 6 | 9 | 10 | 16 | 22 | 23 | 24 | 30 | 36 | 37 | 38 | 39 | 40 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 70 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 90 | 92 | 98 | 99 | 100 | 101 | 103 | 104 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector.xcodeproj/xcuserdata/zhi.zhuang.xcuserdatad/xcschemes/iMessage.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 6 | 9 | 10 | 16 | 22 | 23 | 24 | 30 | 36 | 37 | 38 | 39 | 40 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 70 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 90 | 92 | 98 | 99 | 100 | 101 | 103 | 104 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector.xcodeproj/xcuserdata/zhi.zhuang.xcuserdatad/xcschemes/QYMessage.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 6 | 9 | 10 | 16 | 22 | 23 | 24 | 30 | 36 | 37 | 38 | 39 | 40 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 70 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 90 | 92 | 98 | 99 | 100 | 101 | 103 | 104 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector.xcodeproj/xcuserdata/zhi.zhuang.xcuserdatad/xcschemes/表情包测试.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 6 | 9 | 10 | 16 | 22 | 23 | 24 | 30 | 36 | 37 | 38 | 39 | 40 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 70 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 90 | 92 | 98 | 99 | 100 | 101 | 103 | 104 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/KVOTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // KVOTest.m 3 | // QYCrashProtector 4 | // 5 | // Created by Zhi Zhuang on 2017/10/20. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import "KVOTest.h" 10 | #import 11 | 12 | @implementation NSObject (Test) 13 | 14 | 15 | 16 | +(void)doSw 17 | { 18 | static dispatch_once_t onceToken; 19 | dispatch_once(&onceToken, ^{ 20 | 21 | [self swizzlingInstance:self orginalMethod:NSSelectorFromString(@"addObserver:forKeyPath:options:context:") replaceMethod:NSSelectorFromString(@"qiyes_addObserver:forKeyPath:options:context:")]; 22 | 23 | }); 24 | } 25 | 26 | 27 | +(BOOL)swizzlingInstance:(Class)clz orginalMethod:(SEL)originalSelector replaceMethod:(SEL)replaceSelector{ 28 | 29 | Method original = class_getInstanceMethod(clz, originalSelector); 30 | Method replace = class_getInstanceMethod(clz, replaceSelector); 31 | BOOL didAddMethod = 32 | class_addMethod(clz, 33 | originalSelector, 34 | method_getImplementation(replace), 35 | method_getTypeEncoding(replace)); 36 | 37 | if (didAddMethod) { 38 | class_replaceMethod(clz, 39 | replaceSelector, 40 | method_getImplementation(original), 41 | method_getTypeEncoding(original)); 42 | } else { 43 | method_exchangeImplementations(original, replace); 44 | } 45 | return YES; 46 | } 47 | 48 | - (KVODelegate *)KVO_Proxy 49 | { 50 | id proxy = objc_getAssociatedObject(self, @"KVODelegate_Key"); 51 | 52 | if (nil == proxy) { 53 | proxy = [[KVODelegate alloc] init]; 54 | self.KVO_Proxy = proxy; 55 | NSLog(@"KVODelegate_Key"); 56 | } 57 | 58 | return proxy; 59 | } 60 | 61 | - (void)setKVO_Proxy:(KVODelegate *)proxy 62 | { 63 | objc_setAssociatedObject(self, @"KVODelegate_Key", proxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 64 | } 65 | 66 | -(void)qiye_dealloc 67 | { 68 | 69 | [self qiye_dealloc]; 70 | } 71 | 72 | #pragma KVO 73 | - (void)qiyes_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context 74 | { 75 | NSLog(@"qiyes_addObserver:%@",self); 76 | [self.KVO_Proxy setSelf:observer]; 77 | [self qiyes_addObserver:self.KVO_Proxy forKeyPath:keyPath options:options context:context]; 78 | } 79 | 80 | @end 81 | 82 | @implementation KVOTest{ 83 | KVODelegate * dele; 84 | KVOObj * obj; 85 | } 86 | 87 | 88 | -(BOOL)swizzlingInstance:(Class)clz orginalMethod:(SEL)originalSelector replaceMethod:(SEL)replaceSelector{ 89 | 90 | Method original = class_getInstanceMethod(clz, originalSelector); 91 | Method replace = class_getInstanceMethod(clz, replaceSelector); 92 | BOOL didAddMethod = 93 | class_addMethod(clz, 94 | originalSelector, 95 | method_getImplementation(replace), 96 | method_getTypeEncoding(replace)); 97 | 98 | if (didAddMethod) { 99 | class_replaceMethod(clz, 100 | replaceSelector, 101 | method_getImplementation(original), 102 | method_getTypeEncoding(original)); 103 | } else { 104 | method_exchangeImplementations(original, replace); 105 | } 106 | return YES; 107 | } 108 | 109 | -(void)doSwizzling 110 | { 111 | [NSObject doSw]; 112 | // [self swizzlingInstance:[self class] orginalMethod:NSSelectorFromString(@"addObserver:forKeyPath:options:context:") replaceMethod:NSSelectorFromString(@"qiye_addObserver:forKeyPath:options:context:")]; 113 | } 114 | 115 | #pragma KVO 116 | //- (void)qiye_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context 117 | //{ 118 | // dele = [KVODelegate new]; 119 | // [dele setSelf:observer]; 120 | // [self qiye_addObserver:dele forKeyPath:keyPath options:options context:context]; 121 | //} 122 | 123 | 124 | -(void)doAddObserver 125 | { 126 | NSLog(@"addObserver"); 127 | obj = [KVOObj new]; 128 | 129 | [self addObserver:self forKeyPath:@"kvoName" options:NSKeyValueObservingOptionNew context:nil]; 130 | [obj addObserver:self forKeyPath:@"kvoNames" options:NSKeyValueObservingOptionNew context:nil]; 131 | 132 | self.kvoName = @"ddsdsddssds"; 133 | obj.kvoNames = @"objobjobj"; 134 | } 135 | 136 | - (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary *)change context:(nullable void *)context{ 137 | NSLog(@"observeValueForKeyPath:%@",change); 138 | } 139 | 140 | -(void)dealloc 141 | { 142 | [self removeObserver:[self valueForKey:@"KVO_Proxy"] forKeyPath:@"kvoName"]; 143 | [obj removeObserver:[obj valueForKey:@"KVO_Proxy"] forKeyPath:@"kvoNames"]; 144 | NSLog(@"kvoName dealloc"); 145 | } 146 | 147 | @end 148 | 149 | @implementation KVODelegate{ 150 | __weak NSObject * ktest; 151 | } 152 | 153 | -(void)setSelf:(NSObject*) weak 154 | { 155 | ktest = weak; 156 | } 157 | 158 | - (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary *)change context:(nullable void *)context{ 159 | NSLog(@"KVODelegate - observeValueForKeyPath:%@",change); 160 | if(ktest) [ktest observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 161 | } 162 | 163 | -(void)dealloc 164 | { 165 | 166 | NSLog(@"KVODelegate - kvoName dealloc"); 167 | } 168 | @end 169 | 170 | 171 | @implementation KVOObj 172 | @end 173 | 174 | 175 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 01188AD01F68DC5200FC101E /* Test.m in Sources */ = {isa = PBXBuildFile; fileRef = 01188ACF1F68DC5200FC101E /* Test.m */; }; 11 | 01188AD31F6920C900FC101E /* SwizzlingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 01188AD21F6920C900FC101E /* SwizzlingTest.m */; }; 12 | 0157BEF31F972C740056FB64 /* AClass.m in Sources */ = {isa = PBXBuildFile; fileRef = 0157BEF21F972C740056FB64 /* AClass.m */; }; 13 | 0157BEF61F99DC8C0056FB64 /* KVOTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0157BEF51F99DC8C0056FB64 /* KVOTest.m */; }; 14 | 0157BEF91F9ED7970056FB64 /* BClass.m in Sources */ = {isa = PBXBuildFile; fileRef = 0157BEF81F9ED7970056FB64 /* BClass.m */; }; 15 | 01F021ED1F5D4B0300BDB21E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 01F021EC1F5D4B0300BDB21E /* main.m */; }; 16 | 01F021F01F5D4B0300BDB21E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 01F021EF1F5D4B0300BDB21E /* AppDelegate.m */; }; 17 | 01F021F81F5D4B0300BDB21E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01F021F71F5D4B0300BDB21E /* Assets.xcassets */; }; 18 | 01F021FB1F5D4B0300BDB21E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 01F021F91F5D4B0300BDB21E /* LaunchScreen.storyboard */; }; 19 | 01F022051F5D4F3B00BDB21E /* NSObject+CrashProtector.m in Sources */ = {isa = PBXBuildFile; fileRef = 01F022041F5D4F3B00BDB21E /* NSObject+CrashProtector.m */; }; 20 | 01F022081F5D506500BDB21E /* CPManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 01F022071F5D506500BDB21E /* CPManager.m */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXCopyFilesBuildPhase section */ 24 | 0157BF2C1F9F275D0056FB64 /* Embed App Extensions */ = { 25 | isa = PBXCopyFilesBuildPhase; 26 | buildActionMask = 2147483647; 27 | dstPath = ""; 28 | dstSubfolderSpec = 13; 29 | files = ( 30 | ); 31 | name = "Embed App Extensions"; 32 | runOnlyForDeploymentPostprocessing = 0; 33 | }; 34 | /* End PBXCopyFilesBuildPhase section */ 35 | 36 | /* Begin PBXFileReference section */ 37 | 01188ACE1F68DC5200FC101E /* Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Test.h; sourceTree = ""; }; 38 | 01188ACF1F68DC5200FC101E /* Test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Test.m; sourceTree = ""; }; 39 | 01188AD11F6920C900FC101E /* SwizzlingTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwizzlingTest.h; sourceTree = ""; }; 40 | 01188AD21F6920C900FC101E /* SwizzlingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SwizzlingTest.m; sourceTree = ""; }; 41 | 0157BEF11F972C740056FB64 /* AClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AClass.h; sourceTree = ""; }; 42 | 0157BEF21F972C740056FB64 /* AClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AClass.m; sourceTree = ""; }; 43 | 0157BEF41F99DC8C0056FB64 /* KVOTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KVOTest.h; sourceTree = ""; }; 44 | 0157BEF51F99DC8C0056FB64 /* KVOTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KVOTest.m; sourceTree = ""; }; 45 | 0157BEF71F9ED7970056FB64 /* BClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BClass.h; sourceTree = ""; }; 46 | 0157BEF81F9ED7970056FB64 /* BClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BClass.m; sourceTree = ""; }; 47 | 0157BF1A1F9F275C0056FB64 /* Messages.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Messages.framework; path = System/Library/Frameworks/Messages.framework; sourceTree = SDKROOT; }; 48 | 01F021E81F5D4B0300BDB21E /* QYCrashProtector.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = QYCrashProtector.app; sourceTree = BUILT_PRODUCTS_DIR; }; 49 | 01F021EC1F5D4B0300BDB21E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 50 | 01F021EE1F5D4B0300BDB21E /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 51 | 01F021EF1F5D4B0300BDB21E /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 52 | 01F021F71F5D4B0300BDB21E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 53 | 01F021FA1F5D4B0300BDB21E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 54 | 01F021FC1F5D4B0300BDB21E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 55 | 01F022031F5D4F3B00BDB21E /* NSObject+CrashProtector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+CrashProtector.h"; sourceTree = ""; }; 56 | 01F022041F5D4F3B00BDB21E /* NSObject+CrashProtector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+CrashProtector.m"; sourceTree = ""; }; 57 | 01F022061F5D506500BDB21E /* CPManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CPManager.h; sourceTree = ""; }; 58 | 01F022071F5D506500BDB21E /* CPManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CPManager.m; sourceTree = ""; }; 59 | /* End PBXFileReference section */ 60 | 61 | /* Begin PBXFrameworksBuildPhase section */ 62 | 01F021E51F5D4B0200BDB21E /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | /* End PBXFrameworksBuildPhase section */ 70 | 71 | /* Begin PBXGroup section */ 72 | 0157BF191F9F275C0056FB64 /* Frameworks */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 0157BF1A1F9F275C0056FB64 /* Messages.framework */, 76 | ); 77 | name = Frameworks; 78 | sourceTree = ""; 79 | }; 80 | 01F021DF1F5D4B0200BDB21E = { 81 | isa = PBXGroup; 82 | children = ( 83 | 01F021EA1F5D4B0300BDB21E /* QYCrashProtector */, 84 | 0157BF191F9F275C0056FB64 /* Frameworks */, 85 | 01F021E91F5D4B0300BDB21E /* Products */, 86 | ); 87 | sourceTree = ""; 88 | }; 89 | 01F021E91F5D4B0300BDB21E /* Products */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 01F021E81F5D4B0300BDB21E /* QYCrashProtector.app */, 93 | ); 94 | name = Products; 95 | sourceTree = ""; 96 | }; 97 | 01F021EA1F5D4B0300BDB21E /* QYCrashProtector */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 01F022021F5D4B4100BDB21E /* CrashProtector */, 101 | 01F021EE1F5D4B0300BDB21E /* AppDelegate.h */, 102 | 01F021EF1F5D4B0300BDB21E /* AppDelegate.m */, 103 | 01F021F71F5D4B0300BDB21E /* Assets.xcassets */, 104 | 01F021F91F5D4B0300BDB21E /* LaunchScreen.storyboard */, 105 | 01F021FC1F5D4B0300BDB21E /* Info.plist */, 106 | 01F021EB1F5D4B0300BDB21E /* Supporting Files */, 107 | 01188ACE1F68DC5200FC101E /* Test.h */, 108 | 01188ACF1F68DC5200FC101E /* Test.m */, 109 | 01188AD11F6920C900FC101E /* SwizzlingTest.h */, 110 | 01188AD21F6920C900FC101E /* SwizzlingTest.m */, 111 | 0157BEF11F972C740056FB64 /* AClass.h */, 112 | 0157BEF21F972C740056FB64 /* AClass.m */, 113 | 0157BEF41F99DC8C0056FB64 /* KVOTest.h */, 114 | 0157BEF51F99DC8C0056FB64 /* KVOTest.m */, 115 | 0157BEF71F9ED7970056FB64 /* BClass.h */, 116 | 0157BEF81F9ED7970056FB64 /* BClass.m */, 117 | ); 118 | path = QYCrashProtector; 119 | sourceTree = ""; 120 | }; 121 | 01F021EB1F5D4B0300BDB21E /* Supporting Files */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 01F021EC1F5D4B0300BDB21E /* main.m */, 125 | ); 126 | name = "Supporting Files"; 127 | sourceTree = ""; 128 | }; 129 | 01F022021F5D4B4100BDB21E /* CrashProtector */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | 01F022031F5D4F3B00BDB21E /* NSObject+CrashProtector.h */, 133 | 01F022041F5D4F3B00BDB21E /* NSObject+CrashProtector.m */, 134 | 01F022061F5D506500BDB21E /* CPManager.h */, 135 | 01F022071F5D506500BDB21E /* CPManager.m */, 136 | ); 137 | path = CrashProtector; 138 | sourceTree = ""; 139 | }; 140 | /* End PBXGroup section */ 141 | 142 | /* Begin PBXNativeTarget section */ 143 | 01F021E71F5D4B0200BDB21E /* QYCrashProtector */ = { 144 | isa = PBXNativeTarget; 145 | buildConfigurationList = 01F021FF1F5D4B0300BDB21E /* Build configuration list for PBXNativeTarget "QYCrashProtector" */; 146 | buildPhases = ( 147 | 01F021E41F5D4B0200BDB21E /* Sources */, 148 | 01F021E51F5D4B0200BDB21E /* Frameworks */, 149 | 01F021E61F5D4B0200BDB21E /* Resources */, 150 | 0157BF2C1F9F275D0056FB64 /* Embed App Extensions */, 151 | ); 152 | buildRules = ( 153 | ); 154 | dependencies = ( 155 | ); 156 | name = QYCrashProtector; 157 | productName = QYCrashProtector; 158 | productReference = 01F021E81F5D4B0300BDB21E /* QYCrashProtector.app */; 159 | productType = "com.apple.product-type.application"; 160 | }; 161 | /* End PBXNativeTarget section */ 162 | 163 | /* Begin PBXProject section */ 164 | 01F021E01F5D4B0200BDB21E /* Project object */ = { 165 | isa = PBXProject; 166 | attributes = { 167 | LastUpgradeCheck = 0830; 168 | ORGANIZATIONNAME = qiye; 169 | TargetAttributes = { 170 | 01F021E71F5D4B0200BDB21E = { 171 | CreatedOnToolsVersion = 8.3.2; 172 | DevelopmentTeam = 52L8S4EZRD; 173 | ProvisioningStyle = Manual; 174 | }; 175 | }; 176 | }; 177 | buildConfigurationList = 01F021E31F5D4B0200BDB21E /* Build configuration list for PBXProject "QYCrashProtector" */; 178 | compatibilityVersion = "Xcode 3.2"; 179 | developmentRegion = English; 180 | hasScannedForEncodings = 0; 181 | knownRegions = ( 182 | English, 183 | en, 184 | Base, 185 | ); 186 | mainGroup = 01F021DF1F5D4B0200BDB21E; 187 | productRefGroup = 01F021E91F5D4B0300BDB21E /* Products */; 188 | projectDirPath = ""; 189 | projectRoot = ""; 190 | targets = ( 191 | 01F021E71F5D4B0200BDB21E /* QYCrashProtector */, 192 | ); 193 | }; 194 | /* End PBXProject section */ 195 | 196 | /* Begin PBXResourcesBuildPhase section */ 197 | 01F021E61F5D4B0200BDB21E /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | 01F021FB1F5D4B0300BDB21E /* LaunchScreen.storyboard in Resources */, 202 | 01F021F81F5D4B0300BDB21E /* Assets.xcassets in Resources */, 203 | ); 204 | runOnlyForDeploymentPostprocessing = 0; 205 | }; 206 | /* End PBXResourcesBuildPhase section */ 207 | 208 | /* Begin PBXSourcesBuildPhase section */ 209 | 01F021E41F5D4B0200BDB21E /* Sources */ = { 210 | isa = PBXSourcesBuildPhase; 211 | buildActionMask = 2147483647; 212 | files = ( 213 | 01F021F01F5D4B0300BDB21E /* AppDelegate.m in Sources */, 214 | 0157BEF31F972C740056FB64 /* AClass.m in Sources */, 215 | 01F022081F5D506500BDB21E /* CPManager.m in Sources */, 216 | 01188AD01F68DC5200FC101E /* Test.m in Sources */, 217 | 0157BEF61F99DC8C0056FB64 /* KVOTest.m in Sources */, 218 | 01F022051F5D4F3B00BDB21E /* NSObject+CrashProtector.m in Sources */, 219 | 01F021ED1F5D4B0300BDB21E /* main.m in Sources */, 220 | 01188AD31F6920C900FC101E /* SwizzlingTest.m in Sources */, 221 | 0157BEF91F9ED7970056FB64 /* BClass.m in Sources */, 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | }; 225 | /* End PBXSourcesBuildPhase section */ 226 | 227 | /* Begin PBXVariantGroup section */ 228 | 01F021F91F5D4B0300BDB21E /* LaunchScreen.storyboard */ = { 229 | isa = PBXVariantGroup; 230 | children = ( 231 | 01F021FA1F5D4B0300BDB21E /* Base */, 232 | ); 233 | name = LaunchScreen.storyboard; 234 | sourceTree = ""; 235 | }; 236 | /* End PBXVariantGroup section */ 237 | 238 | /* Begin XCBuildConfiguration section */ 239 | 01F021FD1F5D4B0300BDB21E /* Debug */ = { 240 | isa = XCBuildConfiguration; 241 | buildSettings = { 242 | ALWAYS_SEARCH_USER_PATHS = NO; 243 | CLANG_ANALYZER_NONNULL = YES; 244 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 245 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 246 | CLANG_CXX_LIBRARY = "libc++"; 247 | CLANG_ENABLE_MODULES = YES; 248 | CLANG_ENABLE_OBJC_ARC = YES; 249 | CLANG_WARN_BOOL_CONVERSION = YES; 250 | CLANG_WARN_CONSTANT_CONVERSION = YES; 251 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 252 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 253 | CLANG_WARN_EMPTY_BODY = YES; 254 | CLANG_WARN_ENUM_CONVERSION = YES; 255 | CLANG_WARN_INFINITE_RECURSION = YES; 256 | CLANG_WARN_INT_CONVERSION = YES; 257 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 258 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 259 | CLANG_WARN_UNREACHABLE_CODE = YES; 260 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 261 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 262 | COPY_PHASE_STRIP = NO; 263 | DEBUG_INFORMATION_FORMAT = dwarf; 264 | ENABLE_STRICT_OBJC_MSGSEND = YES; 265 | ENABLE_TESTABILITY = YES; 266 | GCC_C_LANGUAGE_STANDARD = gnu99; 267 | GCC_DYNAMIC_NO_PIC = NO; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_OPTIMIZATION_LEVEL = 0; 270 | GCC_PREPROCESSOR_DEFINITIONS = ( 271 | "DEBUG=1", 272 | "$(inherited)", 273 | ); 274 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 275 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 276 | GCC_WARN_UNDECLARED_SELECTOR = YES; 277 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 278 | GCC_WARN_UNUSED_FUNCTION = YES; 279 | GCC_WARN_UNUSED_VARIABLE = YES; 280 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 281 | MTL_ENABLE_DEBUG_INFO = YES; 282 | ONLY_ACTIVE_ARCH = YES; 283 | SDKROOT = iphoneos; 284 | }; 285 | name = Debug; 286 | }; 287 | 01F021FE1F5D4B0300BDB21E /* Release */ = { 288 | isa = XCBuildConfiguration; 289 | buildSettings = { 290 | ALWAYS_SEARCH_USER_PATHS = NO; 291 | CLANG_ANALYZER_NONNULL = YES; 292 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 293 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 294 | CLANG_CXX_LIBRARY = "libc++"; 295 | CLANG_ENABLE_MODULES = YES; 296 | CLANG_ENABLE_OBJC_ARC = YES; 297 | CLANG_WARN_BOOL_CONVERSION = YES; 298 | CLANG_WARN_CONSTANT_CONVERSION = YES; 299 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 300 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 301 | CLANG_WARN_EMPTY_BODY = YES; 302 | CLANG_WARN_ENUM_CONVERSION = YES; 303 | CLANG_WARN_INFINITE_RECURSION = YES; 304 | CLANG_WARN_INT_CONVERSION = YES; 305 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 306 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 307 | CLANG_WARN_UNREACHABLE_CODE = YES; 308 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 309 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 310 | COPY_PHASE_STRIP = NO; 311 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 312 | ENABLE_NS_ASSERTIONS = NO; 313 | ENABLE_STRICT_OBJC_MSGSEND = YES; 314 | GCC_C_LANGUAGE_STANDARD = gnu99; 315 | GCC_NO_COMMON_BLOCKS = YES; 316 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 317 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 318 | GCC_WARN_UNDECLARED_SELECTOR = YES; 319 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 320 | GCC_WARN_UNUSED_FUNCTION = YES; 321 | GCC_WARN_UNUSED_VARIABLE = YES; 322 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 323 | MTL_ENABLE_DEBUG_INFO = NO; 324 | SDKROOT = iphoneos; 325 | VALIDATE_PRODUCT = YES; 326 | }; 327 | name = Release; 328 | }; 329 | 01F022001F5D4B0300BDB21E /* Debug */ = { 330 | isa = XCBuildConfiguration; 331 | buildSettings = { 332 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 333 | CODE_SIGN_IDENTITY = "iPhone Developer"; 334 | CODE_SIGN_STYLE = Manual; 335 | DEVELOPMENT_TEAM = 52L8S4EZRD; 336 | INFOPLIST_FILE = QYCrashProtector/Info.plist; 337 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 338 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 339 | PRODUCT_BUNDLE_IDENTIFIER = com.denachina.pickle; 340 | PRODUCT_NAME = "$(TARGET_NAME)"; 341 | PROVISIONING_PROFILE = "5b05b63d-6867-490d-a9d9-0c299d96d9ea"; 342 | PROVISIONING_PROFILE_SPECIFIER = wildcard_zy_dev; 343 | }; 344 | name = Debug; 345 | }; 346 | 01F022011F5D4B0300BDB21E /* Release */ = { 347 | isa = XCBuildConfiguration; 348 | buildSettings = { 349 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 350 | CODE_SIGN_IDENTITY = "iPhone Developer"; 351 | CODE_SIGN_STYLE = Manual; 352 | DEVELOPMENT_TEAM = 52L8S4EZRD; 353 | INFOPLIST_FILE = QYCrashProtector/Info.plist; 354 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 355 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 356 | PRODUCT_BUNDLE_IDENTIFIER = com.denachina.pickle; 357 | PRODUCT_NAME = "$(TARGET_NAME)"; 358 | PROVISIONING_PROFILE = "5b05b63d-6867-490d-a9d9-0c299d96d9ea"; 359 | PROVISIONING_PROFILE_SPECIFIER = wildcard_zy_dev; 360 | }; 361 | name = Release; 362 | }; 363 | /* End XCBuildConfiguration section */ 364 | 365 | /* Begin XCConfigurationList section */ 366 | 01F021E31F5D4B0200BDB21E /* Build configuration list for PBXProject "QYCrashProtector" */ = { 367 | isa = XCConfigurationList; 368 | buildConfigurations = ( 369 | 01F021FD1F5D4B0300BDB21E /* Debug */, 370 | 01F021FE1F5D4B0300BDB21E /* Release */, 371 | ); 372 | defaultConfigurationIsVisible = 0; 373 | defaultConfigurationName = Release; 374 | }; 375 | 01F021FF1F5D4B0300BDB21E /* Build configuration list for PBXNativeTarget "QYCrashProtector" */ = { 376 | isa = XCConfigurationList; 377 | buildConfigurations = ( 378 | 01F022001F5D4B0300BDB21E /* Debug */, 379 | 01F022011F5D4B0300BDB21E /* Release */, 380 | ); 381 | defaultConfigurationIsVisible = 0; 382 | defaultConfigurationName = Release; 383 | }; 384 | /* End XCConfigurationList section */ 385 | }; 386 | rootObject = 01F021E01F5D4B0200BDB21E /* Project object */; 387 | } 388 | -------------------------------------------------------------------------------- /iOS/QYCrashProtector/QYCrashProtector/CrashProtector/NSObject+CrashProtector.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+CrashProtector.m 3 | // QYCrashProtector 4 | // 5 | // Created by Zhi Zhuang on 2017/9/4. 6 | // Copyright © 2017年 qiye. All rights reserved. 7 | // 8 | 9 | #import "NSObject+CrashProtector.h" 10 | #import 11 | #import 12 | 13 | // CrashProxy 14 | // get crash message 15 | @implementation CrashProxy 16 | 17 | - (id)getCrashMsg{ 18 | NSLog(@"%@",_crashMsg); 19 | return nil; 20 | } 21 | 22 | @end 23 | 24 | //----------------------------------------------------------------------------------------------------------------------------- 25 | // NSObject (CrashProtector) 26 | // fix "unrecognized selector" ,"KVC" 27 | static void *NSObjectKVOProxyKey = &NSObjectKVOProxyKey; 28 | @implementation NSObject (CrashProtector) 29 | 30 | + (void)openCP 31 | { 32 | static dispatch_once_t onceToken; 33 | dispatch_once(&onceToken, ^{ 34 | [self swizzlingInstance:objc_getClass("__NSPlaceholderDictionary") orginalMethod:@selector(initWithObjects:forKeys:count:) replaceMethod:NSSelectorFromString(@"qiye_initWithObjects:forKeys:count:")]; 35 | 36 | [self swizzlingInstance:objc_getClass("__NSPlaceholderDictionary") orginalMethod:@selector(dictionaryWithObjects:forKeys:count:) replaceMethod:NSSelectorFromString(@"qiye_dictionaryWithObjects:forKeys:count:")]; 37 | 38 | [self swizzlingInstance:objc_getClass("__NSDictionaryM") orginalMethod:@selector(setObject:forKey:) replaceMethod:NSSelectorFromString(@"qiye_setObject:forKey:")]; 39 | 40 | [self swizzlingInstance:objc_getClass("__NSPlaceholderArray") orginalMethod:@selector(initWithObjects:count:) replaceMethod:NSSelectorFromString(@"qiye_initWithObjects:count:")]; 41 | 42 | [self swizzlingInstance:objc_getClass("__NSArrayI") orginalMethod:@selector(objectAtIndex:) replaceMethod:NSSelectorFromString(@"qiye_objectAtIndex:")]; 43 | 44 | [self swizzlingInstance:objc_getClass("__NSArrayI") orginalMethod:@selector(objectAtIndexedSubscript:) replaceMethod:NSSelectorFromString(@"qiye_objectAtIndexedSubscript:")]; 45 | 46 | [self swizzlingInstance:objc_getClass("__NSArrayM") orginalMethod:@selector(addObject:) replaceMethod:NSSelectorFromString(@"qiye_addObject:")]; 47 | 48 | [self swizzlingInstance:objc_getClass("__NSArrayM") orginalMethod:@selector(insertObject:atIndex:) replaceMethod:NSSelectorFromString(@"qiye_insertObject:atIndex:")]; 49 | 50 | [self swizzlingInstance:objc_getClass("__NSArrayM") orginalMethod:@selector(objectAtIndex:) replaceMethod:NSSelectorFromString(@"qiye_objectAtIndex:")]; 51 | 52 | [self swizzlingInstance:objc_getClass("__NSArrayM") orginalMethod:@selector(objectAtIndexedSubscript:) replaceMethod:NSSelectorFromString(@"qiye_objectAtIndexedSubscript:")]; 53 | 54 | [self swizzlingInstance:objc_getClass("NSPlaceholderString") orginalMethod:@selector(initWithString:) replaceMethod:NSSelectorFromString(@"qiye_initWithString:")]; 55 | 56 | [self swizzlingInstance:objc_getClass("__NSCFConstantString") orginalMethod:@selector(hasSuffix:) replaceMethod:NSSelectorFromString(@"qiye_hasSuffix:")]; 57 | 58 | [self swizzlingInstance:objc_getClass("__NSCFConstantString") orginalMethod:@selector(hasPrefix:) replaceMethod:NSSelectorFromString(@"qiye_hasPrefix:")]; 59 | 60 | [self swizzlingInstance:objc_getClass("NSPlaceholderMutableString") orginalMethod:@selector(initWithString:) replaceMethod:NSSelectorFromString(@"qiye_initWithString:")]; 61 | 62 | [self swizzlingClass:objc_getClass("NSTimer") replaceClassMethod:NSSelectorFromString(@"scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:") withMethod:NSSelectorFromString(@"qiye_scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:")]; 63 | 64 | [self swizzlingClass:objc_getClass("NSTimer") replaceClassMethod:@selector(timerWithTimeInterval:target:selector:userInfo:repeats:) withMethod:NSSelectorFromString(@"qiye_timerWithTimeInterval:target:selector:userInfo:repeats:")]; 65 | 66 | [self swizzlingInstance:objc_getClass("NSNotificationCenter") orginalMethod:NSSelectorFromString(@"addObserver:selector:name:object:") replaceMethod:NSSelectorFromString(@"qiye_addObserver:selector:name:object:")]; 67 | 68 | [self swizzlingInstance:self orginalMethod:NSSelectorFromString(@"dealloc") replaceMethod:NSSelectorFromString(@"qiye_dealloc")]; 69 | 70 | //暂时注释kvo预防,该逻辑在 xcode9.2 真机测试会 crash 71 | 72 | [self swizzlingInstance:self orginalMethod:NSSelectorFromString(@"addObserver:forKeyPath:options:context:") replaceMethod:NSSelectorFromString(@"qiye_addObserver:forKeyPath:options:context:")]; 73 | 74 | [self swizzlingInstance:self orginalMethod:NSSelectorFromString(@"removeObserver:forKeyPath:") replaceMethod:NSSelectorFromString(@"qiye_removeObserver:forKeyPath:")]; 75 | 76 | }); 77 | } 78 | 79 | #pragma load 80 | +(void)load 81 | { 82 | if (!CP_OPEN) { 83 | NSLog(@"CrashProtector close !"); 84 | return; 85 | } 86 | NSLog(@"CrashProtector open !"); 87 | [self openCP]; 88 | } 89 | 90 | //在进行方法swizzing时候,一定要注意类簇 ,比如 NSArray NSDictionary 等。 91 | + (BOOL)swizzlingInstanceMethod:(SEL)originalSelector replaceMethod:(SEL)replaceSelector 92 | { 93 | return [self swizzlingInstance:self orginalMethod:originalSelector replaceMethod:replaceSelector]; 94 | } 95 | 96 | +(BOOL)swizzlingInstance:(Class)clz orginalMethod:(SEL)originalSelector replaceMethod:(SEL)replaceSelector{ 97 | 98 | Method original = class_getInstanceMethod(clz, originalSelector); 99 | Method replace = class_getInstanceMethod(clz, replaceSelector); 100 | BOOL didAddMethod = 101 | class_addMethod(clz, 102 | originalSelector, 103 | method_getImplementation(replace), 104 | method_getTypeEncoding(replace)); 105 | 106 | if (didAddMethod) { 107 | class_replaceMethod(clz, 108 | replaceSelector, 109 | method_getImplementation(original), 110 | method_getTypeEncoding(original)); 111 | } else { 112 | method_exchangeImplementations(original, replace); 113 | } 114 | return YES; 115 | } 116 | 117 | + (BOOL)swizzlingClass:(Class)klass replaceClassMethod:(SEL)originalSelector withMethod:(SEL)replaceSelector 118 | { 119 | if (!klass || !originalSelector || !replaceSelector) { 120 | NSLog(@"Nil Parameter(s) found when swizzling."); 121 | return NO; 122 | } 123 | 124 | Method swizzledMethod = class_getClassMethod(self, replaceSelector); 125 | 126 | if ([klass respondsToSelector:originalSelector]) { 127 | Class metaClass = objc_getMetaClass(class_getName(klass)); 128 | BOOL didAdd = class_addMethod(metaClass, 129 | replaceSelector, 130 | method_getImplementation(swizzledMethod), 131 | method_getTypeEncoding(swizzledMethod)); 132 | 133 | if (didAdd) { 134 | method_exchangeImplementations(class_getInstanceMethod(metaClass, originalSelector), 135 | class_getInstanceMethod(metaClass, replaceSelector)); 136 | } 137 | }else{ 138 | NSLog(@"Swizzling Method(s) not found while swizzling class %@.", NSStringFromClass(klass)); 139 | return NO; 140 | } 141 | return YES; 142 | } 143 | 144 | 145 | #pragma clang diagnostic push 146 | #pragma clang diagnostic ignored"-Wobjc-protocol-method-implementation" 147 | - (id)forwardingTargetForSelector:(SEL)aSelector 148 | { 149 | NSString *methodName = NSStringFromSelector(aSelector); 150 | if ([NSStringFromClass([self class]) hasPrefix:@"_"] || [self isKindOfClass:NSClassFromString(@"UITextInputController")] || [NSStringFromClass([self class]) hasPrefix:@"UIKeyboard"] || [methodName isEqualToString:@"dealloc"]) { 151 | 152 | return nil; 153 | } 154 | 155 | CrashProxy * crashProxy = [CrashProxy new]; 156 | crashProxy.crashMsg =[NSString stringWithFormat:@"CrashProtector: [%@ %p %@]: unrecognized selector sent to instance",NSStringFromClass([self class]),self,NSStringFromSelector(aSelector)]; 157 | class_addMethod([CrashProxy class], aSelector, [crashProxy methodForSelector:@selector(getCrashMsg)], "v@:"); 158 | 159 | return crashProxy; 160 | } 161 | #pragma clang diagnostic pop 162 | 163 | 164 | #pragma KVC Protect 165 | -(void)setNilValueForKey:(NSString *)key 166 | { 167 | NSLog(@"need log msg"); 168 | } 169 | 170 | -(void)setValue:(id)value forUndefinedKey:(NSString *)key 171 | { 172 | NSLog(@"need log msg"); 173 | } 174 | 175 | - (nullable id)valueForUndefinedKey:(NSString *)key{ 176 | NSLog(@"need log msg"); 177 | return self; 178 | } 179 | 180 | #pragma NSNotification 181 | -(void)qiye_addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject 182 | { 183 | [observer setIsNSNotification:YES]; 184 | [self qiye_addObserver:observer selector:aSelector name:aName object:anObject]; 185 | } 186 | 187 | -(void)qiye_dealloc 188 | { 189 | if ([self isNSNotification]) { 190 | NSLog(@"[Notification] need log msg"); 191 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 192 | } 193 | 194 | [self qiye_dealloc]; 195 | } 196 | 197 | static const char *isNSNotification = "isNSNotification"; 198 | 199 | -(void)setIsNSNotification:(BOOL)yesOrNo 200 | { 201 | objc_setAssociatedObject(self, isNSNotification, @(yesOrNo), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 202 | } 203 | 204 | -(BOOL)isNSNotification 205 | { 206 | NSNumber *number = objc_getAssociatedObject(self, isNSNotification);; 207 | return [number boolValue]; 208 | } 209 | 210 | #pragma KVO 211 | - (void)qiye_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context 212 | { 213 | CPKVOInfo * kvoInfo = [[CPKVOInfo alloc] initWithKeyPath:keyPath options:options context:context]; 214 | __weak typeof(self) wkself = self; 215 | if([self.KVOProxy addKVOinfo:wkself info:kvoInfo]){ 216 | [self qiye_addObserver:self.KVOProxy forKeyPath:keyPath options:options context:context]; 217 | }else{ 218 | NSLog(@"KVO is more"); 219 | } 220 | } 221 | 222 | - (void)qiye_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath 223 | { 224 | NSLog(@"qiye_removeObserver"); 225 | [self.KVOProxy removeKVOinfo:self keyPath:keyPath block:^{ 226 | [self qiye_removeObserver:observer forKeyPath:keyPath]; 227 | }]; 228 | } 229 | 230 | - (KVOProxy *)KVOProxy 231 | { 232 | id proxy = objc_getAssociatedObject(self, NSObjectKVOProxyKey); 233 | 234 | if (nil == proxy) { 235 | proxy = [[KVOProxy alloc] init]; 236 | self.KVOProxy = proxy; 237 | } 238 | 239 | return proxy; 240 | } 241 | 242 | - (void)setKVOProxy:(KVOProxy *)proxy 243 | { 244 | objc_setAssociatedObject(self, NSObjectKVOProxyKey, proxy, OBJC_ASSOCIATION_ASSIGN); 245 | } 246 | 247 | @end 248 | 249 | //----------------------------------------------------------------------------------------------------------------------------- 250 | // NSDictionary (CrashProtector) 251 | // fix 252 | @implementation NSDictionary (CrashProtector) 253 | 254 | - (instancetype)qiye_initWithObjects:(const id _Nonnull __unsafe_unretained *)objects forKeys:(const id _Nonnull __unsafe_unretained *)keys count:(NSUInteger)cnt{ 255 | id safeObjects[cnt]; 256 | id safeKeys[cnt]; 257 | 258 | NSUInteger j = 0; 259 | for (NSUInteger i = 0; i < cnt ; i++) { 260 | id key = keys[i]; 261 | id obj = objects[i]; 262 | if (!key || !obj) { 263 | NSLog(@"need log msg"); 264 | 265 | continue; 266 | } 267 | safeObjects[j] = obj; 268 | safeKeys[j] = key; 269 | j++; 270 | } 271 | return [self qiye_initWithObjects:safeObjects forKeys:safeKeys count:j]; 272 | } 273 | 274 | 275 | + (instancetype)qiye_dictionaryWithObjects:(const id _Nonnull __unsafe_unretained *)objects forKeys:(const id _Nonnull __unsafe_unretained *)keys count:(NSUInteger)cnt 276 | { 277 | id safeObjects[cnt]; 278 | id safeKeys[cnt]; 279 | 280 | NSUInteger j = 0; 281 | for (NSUInteger i = 0; i < cnt ; i++) { 282 | id key = keys[i]; 283 | id obj = objects[i]; 284 | if (!key || !obj) { 285 | NSLog(@"need log msg"); 286 | continue; 287 | } 288 | safeObjects[j] = obj; 289 | safeKeys[j] = key; 290 | j++; 291 | } 292 | return [self qiye_dictionaryWithObjects:safeObjects forKeys:safeKeys count:j]; 293 | } 294 | 295 | @end 296 | 297 | //----------------------------------------------------------------------------------------------------------------------------- 298 | // NSMutableDictionary (CrashProtector) 299 | // fix 300 | @implementation NSMutableDictionary (CrashProtector) 301 | 302 | - (void)qiye_setObject:(nullable id)anObject forKey:(nullable id )aKey{ 303 | if (!anObject || !aKey) { 304 | NSLog(@"need log msg"); 305 | return; 306 | } 307 | [self qiye_setObject:anObject forKey:aKey]; 308 | } 309 | 310 | @end 311 | 312 | //----------------------------------------------------------------------------------------------------------------------------- 313 | // NSArray (CrashProtector) 314 | // fix 315 | @implementation NSArray (CrashProtector) 316 | 317 | - (instancetype)qiye_initWithObjects:(const id _Nonnull [_Nullable])objects count:(NSUInteger)cnt 318 | { 319 | id safeObjects[cnt]; 320 | NSUInteger j = 0; 321 | for (NSUInteger i = 0; i < cnt ; i++) { 322 | id obj = objects[i]; 323 | if ( !obj) { 324 | NSLog(@"need log msg"); 325 | continue; 326 | } 327 | safeObjects[j] = obj; 328 | j++; 329 | } 330 | return [self qiye_initWithObjects:safeObjects count:j]; 331 | } 332 | 333 | - (id)qiye_objectAtIndex:(NSUInteger)index 334 | { 335 | if (index >= self.count) { 336 | NSLog(@"need log msg"); 337 | return nil; 338 | } 339 | return [self qiye_objectAtIndex:index]; 340 | } 341 | 342 | - (id)qiye_objectAtIndexedSubscript:(NSUInteger)index { 343 | if (index >= self.count ) { 344 | NSLog(@"need log msg"); 345 | return nil; 346 | } 347 | return [self qiye_objectAtIndexedSubscript:index]; 348 | } 349 | 350 | @end 351 | 352 | //----------------------------------------------------------------------------------------------------------------------------- 353 | // NSMutableArray (CrashProtector) 354 | // fix 355 | @implementation NSMutableArray (CrashProtector) 356 | 357 | - (void)qiye_addObject:(id)anObject 358 | { 359 | if(nil == anObject){ 360 | NSLog(@"need log msg"); 361 | return ; 362 | } 363 | [self qiye_addObject:anObject]; 364 | } 365 | 366 | - (void)qiye_insertObject:(id)anObject atIndex:(NSUInteger)index 367 | { 368 | if(nil == anObject){ 369 | NSLog(@"need log msg"); 370 | return ; 371 | } 372 | [self qiye_insertObject:anObject atIndex:index]; 373 | } 374 | 375 | - (id)qiye_objectAtIndex:(NSUInteger)index 376 | { 377 | if (index >= self.count) { 378 | NSLog(@"need log msg"); 379 | return nil; 380 | } 381 | return [self qiye_objectAtIndex:index]; 382 | } 383 | 384 | - (id)qiye_objectAtIndexedSubscript:(NSUInteger)index { 385 | if (index >= self.count ) { 386 | NSLog(@"need log msg"); 387 | return nil; 388 | } 389 | return [self qiye_objectAtIndexedSubscript:index]; 390 | } 391 | 392 | @end 393 | 394 | //----------------------------------------------------------------------------------------------------------------------------- 395 | // NSString (CrashProtector) 396 | // fix 397 | @implementation NSString (CrashProtector) 398 | 399 | - (instancetype)qiye_initWithString:(NSString *)aString 400 | { 401 | if(nil == aString){ 402 | NSLog(@"need log msg"); 403 | return nil; 404 | } 405 | return [self qiye_initWithString:aString]; 406 | } 407 | 408 | - (BOOL)qiye_hasPrefix:(NSString *)str 409 | { 410 | if(nil == str){ 411 | NSLog(@"need log msg"); 412 | return NO; 413 | } 414 | return [self qiye_hasPrefix:str]; 415 | } 416 | 417 | - (BOOL)qiye_hasSuffix:(NSString *)str 418 | { 419 | if(nil == str){ 420 | NSLog(@"need log msg"); 421 | return NO; 422 | } 423 | return [self qiye_hasSuffix:str]; 424 | } 425 | 426 | @end 427 | 428 | //----------------------------------------------------------------------------------------------------------------------------- 429 | // NSMutableString (CrashProtector) 430 | // fix 431 | @implementation NSMutableString (CrashProtector) 432 | 433 | - (instancetype)qiye_initWithString:(NSString *)aString 434 | { 435 | if(nil == aString){ 436 | NSLog(@"need log msg"); 437 | return nil; 438 | } 439 | return [self qiye_initWithString:aString]; 440 | } 441 | 442 | @end 443 | 444 | //----------------------------------------------------------------------------------------------------------------------------- 445 | // CPWeakProxy 446 | @implementation CPWeakProxy 447 | 448 | - (instancetype)initWithTarget:(id)target { 449 | _target = target; 450 | return self; 451 | } 452 | 453 | + (instancetype)proxyWithTarget:(id)target { 454 | return [[CPWeakProxy alloc] initWithTarget:target]; 455 | } 456 | 457 | - (id)forwardingTargetForSelector:(SEL)selector { 458 | return _target; 459 | } 460 | 461 | - (void)forwardInvocation:(NSInvocation *)invocation { 462 | void *null = NULL; 463 | [invocation setReturnValue:&null]; 464 | } 465 | 466 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { 467 | return [NSObject instanceMethodSignatureForSelector:@selector(init)]; 468 | } 469 | 470 | - (BOOL)respondsToSelector:(SEL)aSelector { 471 | return [_target respondsToSelector:aSelector]; 472 | } 473 | 474 | - (BOOL)isEqual:(id)object { 475 | return [_target isEqual:object]; 476 | } 477 | 478 | - (NSUInteger)hash { 479 | return [_target hash]; 480 | } 481 | 482 | - (Class)superclass { 483 | return [_target superclass]; 484 | } 485 | 486 | - (Class)class { 487 | return [_target class]; 488 | } 489 | 490 | - (BOOL)isKindOfClass:(Class)aClass { 491 | return [_target isKindOfClass:aClass]; 492 | } 493 | 494 | - (BOOL)isMemberOfClass:(Class)aClass { 495 | return [_target isMemberOfClass:aClass]; 496 | } 497 | 498 | - (BOOL)conformsToProtocol:(Protocol *)aProtocol { 499 | return [_target conformsToProtocol:aProtocol]; 500 | } 501 | 502 | - (BOOL)isProxy { 503 | return YES; 504 | } 505 | 506 | - (NSString *)description { 507 | return [_target description]; 508 | } 509 | 510 | - (NSString *)debugDescription { 511 | return [_target debugDescription]; 512 | } 513 | 514 | @end 515 | 516 | //----------------------------------------------------------------------------------------------------------------------------- 517 | // NSTimer (CrashProtector) 518 | // fix 519 | @implementation NSTimer (CrashProtector) 520 | 521 | 522 | + (NSTimer *)qiye_scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo 523 | { 524 | NSLog(@"qiye_scheduledTimerWithTimeInterval"); 525 | return [self qiye_scheduledTimerWithTimeInterval:ti target:[CPWeakProxy proxyWithTarget:aTarget] selector:aSelector userInfo:userInfo repeats:yesOrNo]; 526 | } 527 | 528 | + (NSTimer *)qiye_timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo{ 529 | NSLog(@"qiye_timerWithTimeInterval"); 530 | return [self qiye_timerWithTimeInterval:ti target:aTarget selector:aSelector userInfo:userInfo repeats:yesOrNo]; 531 | } 532 | @end 533 | 534 | //----------------------------------------------------------------------------------------------------------------------------- 535 | // KVOProxy 536 | // fix 537 | @implementation KVOProxy{ 538 | pthread_mutex_t _mutex; 539 | NSMapTable *> *_objectInfosMap; 540 | } 541 | 542 | 543 | - (instancetype)init 544 | { 545 | self = [super init]; 546 | if (nil != self) { 547 | 548 | _objectInfosMap = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPointerPersonality valueOptions:NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPersonality capacity:0]; 549 | 550 | pthread_mutex_init(&_mutex, NULL); 551 | } 552 | return self; 553 | } 554 | 555 | -(BOOL)addKVOinfo:(id)object info:(CPKVOInfo *)info 556 | { 557 | [self lock]; 558 | 559 | NSMutableSet *infos = [_objectInfosMap objectForKey:object]; 560 | __block BOOL isHas = NO; 561 | [infos enumerateObjectsUsingBlock:^(id _Nonnull obj, BOOL * _Nonnull stop) { 562 | if([[info valueForKey:@"_keyPath"] isEqualToString:[obj valueForKey:@"_keyPath"]]){ 563 | *stop = YES; 564 | isHas = YES; 565 | } 566 | }]; 567 | if(isHas) { 568 | [self unlock]; 569 | return NO ; 570 | } 571 | if(nil == infos){ 572 | infos = [NSMutableSet set]; 573 | [_objectInfosMap setObject:infos forKey:object]; 574 | } 575 | [infos addObject:info]; 576 | [self unlock]; 577 | 578 | return YES; 579 | } 580 | 581 | -(void)removeKVOinfo:(id)object keyPath:(NSString *)keyPath block:(void(^)()) block 582 | { 583 | [self lock]; 584 | NSMutableSet *infos = [_objectInfosMap objectForKey:object]; 585 | __block CPKVOInfo *info; 586 | [infos enumerateObjectsUsingBlock:^(id _Nonnull obj, BOOL * _Nonnull stop) { 587 | if([keyPath isEqualToString:[obj valueForKey:@"_keyPath"]]){ 588 | info = (CPKVOInfo *)obj; 589 | *stop = YES; 590 | } 591 | }]; 592 | 593 | if (nil != info) { 594 | [infos removeObject:info]; 595 | block(); 596 | if (0 == infos.count) { 597 | [_objectInfosMap removeObjectForKey:object]; 598 | } 599 | } 600 | [self unlock]; 601 | } 602 | 603 | -(void)removeAllObserve 604 | { 605 | if (_objectInfosMap) { 606 | NSMapTable *objectInfoMaps = [_objectInfosMap copy]; 607 | for (id object in objectInfoMaps) { 608 | 609 | NSSet *infos = [objectInfoMaps objectForKey:object]; 610 | if(nil==infos || infos.count==0) continue; 611 | [infos enumerateObjectsUsingBlock:^(id _Nonnull obj, BOOL * _Nonnull stop) { 612 | CPKVOInfo *info = (CPKVOInfo *)obj; 613 | [object removeObserver:self forKeyPath:[info valueForKey:@"_keyPath"]]; 614 | }]; 615 | } 616 | [_objectInfosMap removeAllObjects]; 617 | } 618 | } 619 | 620 | - (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary *)change context:(nullable void *)context{ 621 | NSLog(@"KVOProxy - observeValueForKeyPath :%@",change); 622 | __block CPKVOInfo *info ; 623 | { 624 | [self lock]; 625 | NSSet *infos = [_objectInfosMap objectForKey:object]; 626 | [infos enumerateObjectsUsingBlock:^(id _Nonnull obj, BOOL * _Nonnull stop) { 627 | if([keyPath isEqualToString:[obj valueForKey:@"_keyPath"]]){ 628 | info = (CPKVOInfo *)obj; 629 | *stop = YES; 630 | } 631 | }]; 632 | [self unlock]; 633 | } 634 | 635 | if (nil != info) { 636 | [object observeValueForKeyPath:keyPath ofObject:object change:change context:(__bridge void * _Nullable)([info valueForKey:@"_context"])]; 637 | } 638 | } 639 | 640 | -(void)lock 641 | { 642 | pthread_mutex_lock(&_mutex); 643 | } 644 | 645 | -(void)unlock 646 | { 647 | pthread_mutex_unlock(&_mutex); 648 | } 649 | 650 | - (void)dealloc 651 | { 652 | [self removeAllObserve]; 653 | pthread_mutex_destroy(&_mutex); 654 | NSLog(@"KVOProxy dealloc"); 655 | } 656 | 657 | @end 658 | 659 | //----------------------------------------------------------------------------------------------------------------------------- 660 | // CPKVOInfo 661 | @implementation CPKVOInfo{ 662 | @public 663 | NSString *_keyPath; 664 | NSKeyValueObservingOptions _options; 665 | SEL _action; 666 | void *_context; 667 | CPKVONotificationBlock _block; 668 | } 669 | 670 | - (instancetype)initWithKeyPath:(NSString *)keyPath 671 | options:(NSKeyValueObservingOptions)options 672 | block:(nullable CPKVONotificationBlock)block 673 | action:(nullable SEL)action 674 | context:(nullable void *)context 675 | { 676 | self = [super init]; 677 | if (nil != self) { 678 | _block = [block copy]; 679 | _keyPath = [keyPath copy]; 680 | _options = options; 681 | _action = action; 682 | _context = context; 683 | } 684 | return self; 685 | } 686 | 687 | - (instancetype)initWithKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context 688 | { 689 | return [self initWithKeyPath:keyPath options:options block:NULL action:NULL context:context]; 690 | } 691 | 692 | @end 693 | --------------------------------------------------------------------------------