├── 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 |
--------------------------------------------------------------------------------