├── HYExceptionProtector.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── HYExceptionProtector ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── HYExceptionProtector │ ├── HYExcepitionCollector.h │ ├── HYExcepitionCollector.m │ ├── HYExcepitionProtectedProtocol.h │ ├── HYExcepitionProtector.h │ ├── HYExcepitionProtector.m │ ├── HYExcepitionReportor.h │ ├── HYExcepitionReportor.m │ ├── NSArray+ExcepitionProtector.h │ ├── NSArray+ExcepitionProtector.m │ ├── NSDictionary+ExcepitionProtector.h │ ├── NSDictionary+ExcepitionProtector.m │ ├── NSMutableArray+ExcepitionProtector.h │ ├── NSMutableArray+ExcepitionProtector.m │ ├── NSMutableDictionary+ExcepitionProtector.h │ ├── NSMutableDictionary+ExcepitionProtector.m │ ├── NSMutableSet+ExcepitionProtector.h │ ├── NSMutableSet+ExcepitionProtector.m │ ├── NSMutableString+ExcepitionProtector.h │ ├── NSMutableString+ExcepitionProtector.m │ ├── NSNotificationCenter+ExcepitionProtector.h │ ├── NSNotificationCenter+ExcepitionProtector.m │ ├── NSObject+ExcepitionProtectorSet.h │ ├── NSObject+ExcepitionProtectorSet.m │ ├── NSObject+HYHook.h │ ├── NSObject+HYHook.m │ ├── NSObject+KVOCrashProtector.h │ ├── NSObject+KVOCrashProtector.m │ ├── NSObject+UnrecognizedSelectorProtector.h │ ├── NSObject+UnrecognizedSelectorProtector.m │ ├── NSSet+ExcepitionProtector.h │ ├── NSSet+ExcepitionProtector.m │ ├── NSString+ExcepitionProtector.h │ ├── NSString+ExcepitionProtector.m │ ├── NSTimer+ExcepitionProtector.h │ ├── NSTimer+ExcepitionProtector.m │ ├── UIView+ExcepitionProtector.h │ └── UIView+ExcepitionProtector.m ├── HYView.h ├── HYView.m ├── Info.plist ├── Person.h ├── Person.m ├── ViewController.h ├── ViewController.m └── main.m ├── HYExceptionProtectorTests ├── HYExceptionProtectorTests.m └── Info.plist ├── HYExceptionProtectorUITests ├── HYExceptionProtectorUITests.m └── Info.plist └── README.md /HYExceptionProtector.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 290FB20C21D20AC20056A25C /* NSMutableDictionary+ExcepitionProtector.m in Sources */ = {isa = PBXBuildFile; fileRef = 290FB20B21D20AC20056A25C /* NSMutableDictionary+ExcepitionProtector.m */; }; 11 | 290FB20F21D23B350056A25C /* UIView+ExcepitionProtector.m in Sources */ = {isa = PBXBuildFile; fileRef = 290FB20E21D23B350056A25C /* UIView+ExcepitionProtector.m */; }; 12 | 290FB21221D240D30056A25C /* HYView.m in Sources */ = {isa = PBXBuildFile; fileRef = 290FB21121D240D30056A25C /* HYView.m */; }; 13 | 290FB21821D252660056A25C /* NSObject+KVOCrashProtector.m in Sources */ = {isa = PBXBuildFile; fileRef = 290FB21721D252660056A25C /* NSObject+KVOCrashProtector.m */; }; 14 | 29150BEC21D3261900C11A30 /* NSObject+ExcepitionProtectorSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 29150BEB21D3261900C11A30 /* NSObject+ExcepitionProtectorSet.m */; }; 15 | 29150BEF21D32D6600C11A30 /* NSNotificationCenter+ExcepitionProtector.m in Sources */ = {isa = PBXBuildFile; fileRef = 29150BEE21D32D6600C11A30 /* NSNotificationCenter+ExcepitionProtector.m */; }; 16 | 29150BF221D32F1400C11A30 /* NSTimer+ExcepitionProtector.m in Sources */ = {isa = PBXBuildFile; fileRef = 29150BF121D32F1400C11A30 /* NSTimer+ExcepitionProtector.m */; }; 17 | 29150BF521D3875100C11A30 /* NSSet+ExcepitionProtector.m in Sources */ = {isa = PBXBuildFile; fileRef = 29150BF421D3875100C11A30 /* NSSet+ExcepitionProtector.m */; }; 18 | 29150BF821D3876800C11A30 /* NSMutableSet+ExcepitionProtector.m in Sources */ = {isa = PBXBuildFile; fileRef = 29150BF721D3876800C11A30 /* NSMutableSet+ExcepitionProtector.m */; }; 19 | 29150BFB21D3A15B00C11A30 /* NSString+ExcepitionProtector.m in Sources */ = {isa = PBXBuildFile; fileRef = 29150BFA21D3A15B00C11A30 /* NSString+ExcepitionProtector.m */; }; 20 | 29150BFE21D3A17500C11A30 /* NSMutableString+ExcepitionProtector.m in Sources */ = {isa = PBXBuildFile; fileRef = 29150BFD21D3A17500C11A30 /* NSMutableString+ExcepitionProtector.m */; }; 21 | 292B764121CA3D6500CE6281 /* NSObject+UnrecognizedSelectorProtector.m in Sources */ = {isa = PBXBuildFile; fileRef = 292B764021CA3D6500CE6281 /* NSObject+UnrecognizedSelectorProtector.m */; }; 22 | 292B764721CA45F600CE6281 /* Person.m in Sources */ = {isa = PBXBuildFile; fileRef = 292B764621CA45F600CE6281 /* Person.m */; }; 23 | 2961120021CB3C6F0052469B /* NSArray+ExcepitionProtector.m in Sources */ = {isa = PBXBuildFile; fileRef = 296111FF21CB3C6F0052469B /* NSArray+ExcepitionProtector.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 24 | 2961120621CB7DDC0052469B /* NSDictionary+ExcepitionProtector.m in Sources */ = {isa = PBXBuildFile; fileRef = 2961120521CB7DDC0052469B /* NSDictionary+ExcepitionProtector.m */; }; 25 | 2961120921CB81CC0052469B /* NSMutableArray+ExcepitionProtector.m in Sources */ = {isa = PBXBuildFile; fileRef = 2961120821CB81CC0052469B /* NSMutableArray+ExcepitionProtector.m */; }; 26 | 29DE44DA217DB73500728149 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 29DE44D9217DB73500728149 /* AppDelegate.m */; }; 27 | 29DE44DD217DB73500728149 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 29DE44DC217DB73500728149 /* ViewController.m */; }; 28 | 29DE44E0217DB73500728149 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 29DE44DE217DB73500728149 /* Main.storyboard */; }; 29 | 29DE44E2217DB73700728149 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 29DE44E1217DB73700728149 /* Assets.xcassets */; }; 30 | 29DE44E5217DB73700728149 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 29DE44E3217DB73700728149 /* LaunchScreen.storyboard */; }; 31 | 29DE44E8217DB73700728149 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29DE44E7217DB73700728149 /* main.m */; }; 32 | 29DE44F2217DB73700728149 /* HYExceptionProtectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 29DE44F1217DB73700728149 /* HYExceptionProtectorTests.m */; }; 33 | 29DE44FD217DB73700728149 /* HYExceptionProtectorUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 29DE44FC217DB73700728149 /* HYExceptionProtectorUITests.m */; }; 34 | 29DE450D217DC2F500728149 /* NSObject+HYHook.m in Sources */ = {isa = PBXBuildFile; fileRef = 29DE450B217DC2F500728149 /* NSObject+HYHook.m */; }; 35 | 29DE4510217DC32100728149 /* HYExcepitionProtector.m in Sources */ = {isa = PBXBuildFile; fileRef = 29DE450F217DC32100728149 /* HYExcepitionProtector.m */; }; 36 | 29DE451A217DFB2000728149 /* HYExcepitionCollector.m in Sources */ = {isa = PBXBuildFile; fileRef = 29DE4519217DFB2000728149 /* HYExcepitionCollector.m */; }; 37 | 29DE451D217DFB5200728149 /* HYExcepitionReportor.m in Sources */ = {isa = PBXBuildFile; fileRef = 29DE451C217DFB5200728149 /* HYExcepitionReportor.m */; }; 38 | /* End PBXBuildFile section */ 39 | 40 | /* Begin PBXContainerItemProxy section */ 41 | 29DE44EE217DB73700728149 /* PBXContainerItemProxy */ = { 42 | isa = PBXContainerItemProxy; 43 | containerPortal = 29DE44CD217DB73500728149 /* Project object */; 44 | proxyType = 1; 45 | remoteGlobalIDString = 29DE44D4217DB73500728149; 46 | remoteInfo = HYExceptionProtector; 47 | }; 48 | 29DE44F9217DB73700728149 /* PBXContainerItemProxy */ = { 49 | isa = PBXContainerItemProxy; 50 | containerPortal = 29DE44CD217DB73500728149 /* Project object */; 51 | proxyType = 1; 52 | remoteGlobalIDString = 29DE44D4217DB73500728149; 53 | remoteInfo = HYExceptionProtector; 54 | }; 55 | /* End PBXContainerItemProxy section */ 56 | 57 | /* Begin PBXFileReference section */ 58 | 290FB20A21D20AC20056A25C /* NSMutableDictionary+ExcepitionProtector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSMutableDictionary+ExcepitionProtector.h"; sourceTree = ""; }; 59 | 290FB20B21D20AC20056A25C /* NSMutableDictionary+ExcepitionProtector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSMutableDictionary+ExcepitionProtector.m"; sourceTree = ""; }; 60 | 290FB20D21D23B350056A25C /* UIView+ExcepitionProtector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIView+ExcepitionProtector.h"; sourceTree = ""; }; 61 | 290FB20E21D23B350056A25C /* UIView+ExcepitionProtector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIView+ExcepitionProtector.m"; sourceTree = ""; }; 62 | 290FB21021D240D30056A25C /* HYView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HYView.h; sourceTree = ""; }; 63 | 290FB21121D240D30056A25C /* HYView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HYView.m; sourceTree = ""; }; 64 | 290FB21621D252660056A25C /* NSObject+KVOCrashProtector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSObject+KVOCrashProtector.h"; sourceTree = ""; }; 65 | 290FB21721D252660056A25C /* NSObject+KVOCrashProtector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSObject+KVOCrashProtector.m"; sourceTree = ""; }; 66 | 29150BEA21D3261900C11A30 /* NSObject+ExcepitionProtectorSet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSObject+ExcepitionProtectorSet.h"; sourceTree = ""; }; 67 | 29150BEB21D3261900C11A30 /* NSObject+ExcepitionProtectorSet.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSObject+ExcepitionProtectorSet.m"; sourceTree = ""; }; 68 | 29150BED21D32D6600C11A30 /* NSNotificationCenter+ExcepitionProtector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+ExcepitionProtector.h"; sourceTree = ""; }; 69 | 29150BEE21D32D6600C11A30 /* NSNotificationCenter+ExcepitionProtector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSNotificationCenter+ExcepitionProtector.m"; sourceTree = ""; }; 70 | 29150BF021D32F1400C11A30 /* NSTimer+ExcepitionProtector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSTimer+ExcepitionProtector.h"; sourceTree = ""; }; 71 | 29150BF121D32F1400C11A30 /* NSTimer+ExcepitionProtector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSTimer+ExcepitionProtector.m"; sourceTree = ""; }; 72 | 29150BF321D3875100C11A30 /* NSSet+ExcepitionProtector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSSet+ExcepitionProtector.h"; sourceTree = ""; }; 73 | 29150BF421D3875100C11A30 /* NSSet+ExcepitionProtector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSSet+ExcepitionProtector.m"; sourceTree = ""; }; 74 | 29150BF621D3876800C11A30 /* NSMutableSet+ExcepitionProtector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSMutableSet+ExcepitionProtector.h"; sourceTree = ""; }; 75 | 29150BF721D3876800C11A30 /* NSMutableSet+ExcepitionProtector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSMutableSet+ExcepitionProtector.m"; sourceTree = ""; }; 76 | 29150BF921D3A15B00C11A30 /* NSString+ExcepitionProtector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSString+ExcepitionProtector.h"; sourceTree = ""; }; 77 | 29150BFA21D3A15B00C11A30 /* NSString+ExcepitionProtector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSString+ExcepitionProtector.m"; sourceTree = ""; }; 78 | 29150BFC21D3A17500C11A30 /* NSMutableString+ExcepitionProtector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSMutableString+ExcepitionProtector.h"; sourceTree = ""; }; 79 | 29150BFD21D3A17500C11A30 /* NSMutableString+ExcepitionProtector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSMutableString+ExcepitionProtector.m"; sourceTree = ""; }; 80 | 292B763F21CA3D6500CE6281 /* NSObject+UnrecognizedSelectorProtector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSObject+UnrecognizedSelectorProtector.h"; sourceTree = ""; }; 81 | 292B764021CA3D6500CE6281 /* NSObject+UnrecognizedSelectorProtector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSObject+UnrecognizedSelectorProtector.m"; sourceTree = ""; }; 82 | 292B764521CA45F600CE6281 /* Person.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Person.h; sourceTree = ""; }; 83 | 292B764621CA45F600CE6281 /* Person.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Person.m; sourceTree = ""; }; 84 | 296111FE21CB3C6F0052469B /* NSArray+ExcepitionProtector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSArray+ExcepitionProtector.h"; sourceTree = ""; }; 85 | 296111FF21CB3C6F0052469B /* NSArray+ExcepitionProtector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSArray+ExcepitionProtector.m"; sourceTree = ""; }; 86 | 2961120421CB7DDC0052469B /* NSDictionary+ExcepitionProtector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+ExcepitionProtector.h"; sourceTree = ""; }; 87 | 2961120521CB7DDC0052469B /* NSDictionary+ExcepitionProtector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+ExcepitionProtector.m"; sourceTree = ""; }; 88 | 2961120721CB81CC0052469B /* NSMutableArray+ExcepitionProtector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+ExcepitionProtector.h"; sourceTree = ""; }; 89 | 2961120821CB81CC0052469B /* NSMutableArray+ExcepitionProtector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+ExcepitionProtector.m"; sourceTree = ""; }; 90 | 29DE44D5217DB73500728149 /* HYExceptionProtector.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HYExceptionProtector.app; sourceTree = BUILT_PRODUCTS_DIR; }; 91 | 29DE44D8217DB73500728149 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 92 | 29DE44D9217DB73500728149 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 93 | 29DE44DB217DB73500728149 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 94 | 29DE44DC217DB73500728149 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 95 | 29DE44DF217DB73500728149 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 96 | 29DE44E1217DB73700728149 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97 | 29DE44E4217DB73700728149 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 98 | 29DE44E6217DB73700728149 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 99 | 29DE44E7217DB73700728149 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 100 | 29DE44ED217DB73700728149 /* HYExceptionProtectorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HYExceptionProtectorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 101 | 29DE44F1217DB73700728149 /* HYExceptionProtectorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HYExceptionProtectorTests.m; sourceTree = ""; }; 102 | 29DE44F3217DB73700728149 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 103 | 29DE44F8217DB73700728149 /* HYExceptionProtectorUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HYExceptionProtectorUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 104 | 29DE44FC217DB73700728149 /* HYExceptionProtectorUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HYExceptionProtectorUITests.m; sourceTree = ""; }; 105 | 29DE44FE217DB73700728149 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 106 | 29DE450B217DC2F500728149 /* NSObject+HYHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+HYHook.m"; sourceTree = ""; }; 107 | 29DE450C217DC2F500728149 /* NSObject+HYHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+HYHook.h"; sourceTree = ""; }; 108 | 29DE450E217DC32100728149 /* HYExcepitionProtector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HYExcepitionProtector.h; sourceTree = ""; }; 109 | 29DE450F217DC32100728149 /* HYExcepitionProtector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HYExcepitionProtector.m; sourceTree = ""; }; 110 | 29DE4514217DCD4800728149 /* HYExcepitionProtectedProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HYExcepitionProtectedProtocol.h; sourceTree = ""; }; 111 | 29DE4518217DFB2000728149 /* HYExcepitionCollector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HYExcepitionCollector.h; sourceTree = ""; }; 112 | 29DE4519217DFB2000728149 /* HYExcepitionCollector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HYExcepitionCollector.m; sourceTree = ""; }; 113 | 29DE451B217DFB5200728149 /* HYExcepitionReportor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HYExcepitionReportor.h; sourceTree = ""; }; 114 | 29DE451C217DFB5200728149 /* HYExcepitionReportor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HYExcepitionReportor.m; sourceTree = ""; }; 115 | /* End PBXFileReference section */ 116 | 117 | /* Begin PBXFrameworksBuildPhase section */ 118 | 29DE44D2217DB73500728149 /* Frameworks */ = { 119 | isa = PBXFrameworksBuildPhase; 120 | buildActionMask = 2147483647; 121 | files = ( 122 | ); 123 | runOnlyForDeploymentPostprocessing = 0; 124 | }; 125 | 29DE44EA217DB73700728149 /* Frameworks */ = { 126 | isa = PBXFrameworksBuildPhase; 127 | buildActionMask = 2147483647; 128 | files = ( 129 | ); 130 | runOnlyForDeploymentPostprocessing = 0; 131 | }; 132 | 29DE44F5217DB73700728149 /* Frameworks */ = { 133 | isa = PBXFrameworksBuildPhase; 134 | buildActionMask = 2147483647; 135 | files = ( 136 | ); 137 | runOnlyForDeploymentPostprocessing = 0; 138 | }; 139 | /* End PBXFrameworksBuildPhase section */ 140 | 141 | /* Begin PBXGroup section */ 142 | 29DE44CC217DB73500728149 = { 143 | isa = PBXGroup; 144 | children = ( 145 | 29DE44D7217DB73500728149 /* HYExceptionProtector */, 146 | 29DE44F0217DB73700728149 /* HYExceptionProtectorTests */, 147 | 29DE44FB217DB73700728149 /* HYExceptionProtectorUITests */, 148 | 29DE44D6217DB73500728149 /* Products */, 149 | ); 150 | sourceTree = ""; 151 | }; 152 | 29DE44D6217DB73500728149 /* Products */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 29DE44D5217DB73500728149 /* HYExceptionProtector.app */, 156 | 29DE44ED217DB73700728149 /* HYExceptionProtectorTests.xctest */, 157 | 29DE44F8217DB73700728149 /* HYExceptionProtectorUITests.xctest */, 158 | ); 159 | name = Products; 160 | sourceTree = ""; 161 | }; 162 | 29DE44D7217DB73500728149 /* HYExceptionProtector */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | 29DE450A217DB76A00728149 /* HYExceptionProtector */, 166 | 29DE44D8217DB73500728149 /* AppDelegate.h */, 167 | 29DE44D9217DB73500728149 /* AppDelegate.m */, 168 | 29DE44DB217DB73500728149 /* ViewController.h */, 169 | 29DE44DC217DB73500728149 /* ViewController.m */, 170 | 292B764521CA45F600CE6281 /* Person.h */, 171 | 292B764621CA45F600CE6281 /* Person.m */, 172 | 290FB21021D240D30056A25C /* HYView.h */, 173 | 290FB21121D240D30056A25C /* HYView.m */, 174 | 29DE44DE217DB73500728149 /* Main.storyboard */, 175 | 29DE44E1217DB73700728149 /* Assets.xcassets */, 176 | 29DE44E3217DB73700728149 /* LaunchScreen.storyboard */, 177 | 29DE44E6217DB73700728149 /* Info.plist */, 178 | 29DE44E7217DB73700728149 /* main.m */, 179 | ); 180 | path = HYExceptionProtector; 181 | sourceTree = ""; 182 | }; 183 | 29DE44F0217DB73700728149 /* HYExceptionProtectorTests */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | 29DE44F1217DB73700728149 /* HYExceptionProtectorTests.m */, 187 | 29DE44F3217DB73700728149 /* Info.plist */, 188 | ); 189 | path = HYExceptionProtectorTests; 190 | sourceTree = ""; 191 | }; 192 | 29DE44FB217DB73700728149 /* HYExceptionProtectorUITests */ = { 193 | isa = PBXGroup; 194 | children = ( 195 | 29DE44FC217DB73700728149 /* HYExceptionProtectorUITests.m */, 196 | 29DE44FE217DB73700728149 /* Info.plist */, 197 | ); 198 | path = HYExceptionProtectorUITests; 199 | sourceTree = ""; 200 | }; 201 | 29DE450A217DB76A00728149 /* HYExceptionProtector */ = { 202 | isa = PBXGroup; 203 | children = ( 204 | 29DE4514217DCD4800728149 /* HYExcepitionProtectedProtocol.h */, 205 | 29DE450E217DC32100728149 /* HYExcepitionProtector.h */, 206 | 29DE450F217DC32100728149 /* HYExcepitionProtector.m */, 207 | 29DE4518217DFB2000728149 /* HYExcepitionCollector.h */, 208 | 29DE4519217DFB2000728149 /* HYExcepitionCollector.m */, 209 | 29DE451B217DFB5200728149 /* HYExcepitionReportor.h */, 210 | 29DE451C217DFB5200728149 /* HYExcepitionReportor.m */, 211 | 29DE450C217DC2F500728149 /* NSObject+HYHook.h */, 212 | 29DE450B217DC2F500728149 /* NSObject+HYHook.m */, 213 | 292B763F21CA3D6500CE6281 /* NSObject+UnrecognizedSelectorProtector.h */, 214 | 292B764021CA3D6500CE6281 /* NSObject+UnrecognizedSelectorProtector.m */, 215 | 296111FE21CB3C6F0052469B /* NSArray+ExcepitionProtector.h */, 216 | 296111FF21CB3C6F0052469B /* NSArray+ExcepitionProtector.m */, 217 | 2961120721CB81CC0052469B /* NSMutableArray+ExcepitionProtector.h */, 218 | 2961120821CB81CC0052469B /* NSMutableArray+ExcepitionProtector.m */, 219 | 2961120421CB7DDC0052469B /* NSDictionary+ExcepitionProtector.h */, 220 | 2961120521CB7DDC0052469B /* NSDictionary+ExcepitionProtector.m */, 221 | 290FB20A21D20AC20056A25C /* NSMutableDictionary+ExcepitionProtector.h */, 222 | 290FB20B21D20AC20056A25C /* NSMutableDictionary+ExcepitionProtector.m */, 223 | 290FB20D21D23B350056A25C /* UIView+ExcepitionProtector.h */, 224 | 290FB20E21D23B350056A25C /* UIView+ExcepitionProtector.m */, 225 | 290FB21621D252660056A25C /* NSObject+KVOCrashProtector.h */, 226 | 290FB21721D252660056A25C /* NSObject+KVOCrashProtector.m */, 227 | 29150BEA21D3261900C11A30 /* NSObject+ExcepitionProtectorSet.h */, 228 | 29150BEB21D3261900C11A30 /* NSObject+ExcepitionProtectorSet.m */, 229 | 29150BED21D32D6600C11A30 /* NSNotificationCenter+ExcepitionProtector.h */, 230 | 29150BEE21D32D6600C11A30 /* NSNotificationCenter+ExcepitionProtector.m */, 231 | 29150BF021D32F1400C11A30 /* NSTimer+ExcepitionProtector.h */, 232 | 29150BF121D32F1400C11A30 /* NSTimer+ExcepitionProtector.m */, 233 | 29150BF321D3875100C11A30 /* NSSet+ExcepitionProtector.h */, 234 | 29150BF421D3875100C11A30 /* NSSet+ExcepitionProtector.m */, 235 | 29150BF621D3876800C11A30 /* NSMutableSet+ExcepitionProtector.h */, 236 | 29150BF721D3876800C11A30 /* NSMutableSet+ExcepitionProtector.m */, 237 | 29150BF921D3A15B00C11A30 /* NSString+ExcepitionProtector.h */, 238 | 29150BFA21D3A15B00C11A30 /* NSString+ExcepitionProtector.m */, 239 | 29150BFC21D3A17500C11A30 /* NSMutableString+ExcepitionProtector.h */, 240 | 29150BFD21D3A17500C11A30 /* NSMutableString+ExcepitionProtector.m */, 241 | ); 242 | path = HYExceptionProtector; 243 | sourceTree = ""; 244 | }; 245 | /* End PBXGroup section */ 246 | 247 | /* Begin PBXNativeTarget section */ 248 | 29DE44D4217DB73500728149 /* HYExceptionProtector */ = { 249 | isa = PBXNativeTarget; 250 | buildConfigurationList = 29DE4501217DB73700728149 /* Build configuration list for PBXNativeTarget "HYExceptionProtector" */; 251 | buildPhases = ( 252 | 29DE44D1217DB73500728149 /* Sources */, 253 | 29DE44D2217DB73500728149 /* Frameworks */, 254 | 29DE44D3217DB73500728149 /* Resources */, 255 | ); 256 | buildRules = ( 257 | ); 258 | dependencies = ( 259 | ); 260 | name = HYExceptionProtector; 261 | productName = HYExceptionProtector; 262 | productReference = 29DE44D5217DB73500728149 /* HYExceptionProtector.app */; 263 | productType = "com.apple.product-type.application"; 264 | }; 265 | 29DE44EC217DB73700728149 /* HYExceptionProtectorTests */ = { 266 | isa = PBXNativeTarget; 267 | buildConfigurationList = 29DE4504217DB73700728149 /* Build configuration list for PBXNativeTarget "HYExceptionProtectorTests" */; 268 | buildPhases = ( 269 | 29DE44E9217DB73700728149 /* Sources */, 270 | 29DE44EA217DB73700728149 /* Frameworks */, 271 | 29DE44EB217DB73700728149 /* Resources */, 272 | ); 273 | buildRules = ( 274 | ); 275 | dependencies = ( 276 | 29DE44EF217DB73700728149 /* PBXTargetDependency */, 277 | ); 278 | name = HYExceptionProtectorTests; 279 | productName = HYExceptionProtectorTests; 280 | productReference = 29DE44ED217DB73700728149 /* HYExceptionProtectorTests.xctest */; 281 | productType = "com.apple.product-type.bundle.unit-test"; 282 | }; 283 | 29DE44F7217DB73700728149 /* HYExceptionProtectorUITests */ = { 284 | isa = PBXNativeTarget; 285 | buildConfigurationList = 29DE4507217DB73700728149 /* Build configuration list for PBXNativeTarget "HYExceptionProtectorUITests" */; 286 | buildPhases = ( 287 | 29DE44F4217DB73700728149 /* Sources */, 288 | 29DE44F5217DB73700728149 /* Frameworks */, 289 | 29DE44F6217DB73700728149 /* Resources */, 290 | ); 291 | buildRules = ( 292 | ); 293 | dependencies = ( 294 | 29DE44FA217DB73700728149 /* PBXTargetDependency */, 295 | ); 296 | name = HYExceptionProtectorUITests; 297 | productName = HYExceptionProtectorUITests; 298 | productReference = 29DE44F8217DB73700728149 /* HYExceptionProtectorUITests.xctest */; 299 | productType = "com.apple.product-type.bundle.ui-testing"; 300 | }; 301 | /* End PBXNativeTarget section */ 302 | 303 | /* Begin PBXProject section */ 304 | 29DE44CD217DB73500728149 /* Project object */ = { 305 | isa = PBXProject; 306 | attributes = { 307 | LastUpgradeCheck = 1000; 308 | ORGANIZATIONNAME = "张鸿运"; 309 | TargetAttributes = { 310 | 29DE44D4217DB73500728149 = { 311 | CreatedOnToolsVersion = 10.0; 312 | }; 313 | 29DE44EC217DB73700728149 = { 314 | CreatedOnToolsVersion = 10.0; 315 | TestTargetID = 29DE44D4217DB73500728149; 316 | }; 317 | 29DE44F7217DB73700728149 = { 318 | CreatedOnToolsVersion = 10.0; 319 | TestTargetID = 29DE44D4217DB73500728149; 320 | }; 321 | }; 322 | }; 323 | buildConfigurationList = 29DE44D0217DB73500728149 /* Build configuration list for PBXProject "HYExceptionProtector" */; 324 | compatibilityVersion = "Xcode 9.3"; 325 | developmentRegion = en; 326 | hasScannedForEncodings = 0; 327 | knownRegions = ( 328 | en, 329 | Base, 330 | ); 331 | mainGroup = 29DE44CC217DB73500728149; 332 | productRefGroup = 29DE44D6217DB73500728149 /* Products */; 333 | projectDirPath = ""; 334 | projectRoot = ""; 335 | targets = ( 336 | 29DE44D4217DB73500728149 /* HYExceptionProtector */, 337 | 29DE44EC217DB73700728149 /* HYExceptionProtectorTests */, 338 | 29DE44F7217DB73700728149 /* HYExceptionProtectorUITests */, 339 | ); 340 | }; 341 | /* End PBXProject section */ 342 | 343 | /* Begin PBXResourcesBuildPhase section */ 344 | 29DE44D3217DB73500728149 /* Resources */ = { 345 | isa = PBXResourcesBuildPhase; 346 | buildActionMask = 2147483647; 347 | files = ( 348 | 29DE44E5217DB73700728149 /* LaunchScreen.storyboard in Resources */, 349 | 29DE44E2217DB73700728149 /* Assets.xcassets in Resources */, 350 | 29DE44E0217DB73500728149 /* Main.storyboard in Resources */, 351 | ); 352 | runOnlyForDeploymentPostprocessing = 0; 353 | }; 354 | 29DE44EB217DB73700728149 /* Resources */ = { 355 | isa = PBXResourcesBuildPhase; 356 | buildActionMask = 2147483647; 357 | files = ( 358 | ); 359 | runOnlyForDeploymentPostprocessing = 0; 360 | }; 361 | 29DE44F6217DB73700728149 /* Resources */ = { 362 | isa = PBXResourcesBuildPhase; 363 | buildActionMask = 2147483647; 364 | files = ( 365 | ); 366 | runOnlyForDeploymentPostprocessing = 0; 367 | }; 368 | /* End PBXResourcesBuildPhase section */ 369 | 370 | /* Begin PBXSourcesBuildPhase section */ 371 | 29DE44D1217DB73500728149 /* Sources */ = { 372 | isa = PBXSourcesBuildPhase; 373 | buildActionMask = 2147483647; 374 | files = ( 375 | 29150BFB21D3A15B00C11A30 /* NSString+ExcepitionProtector.m in Sources */, 376 | 290FB20C21D20AC20056A25C /* NSMutableDictionary+ExcepitionProtector.m in Sources */, 377 | 29DE44DD217DB73500728149 /* ViewController.m in Sources */, 378 | 292B764121CA3D6500CE6281 /* NSObject+UnrecognizedSelectorProtector.m in Sources */, 379 | 29DE451A217DFB2000728149 /* HYExcepitionCollector.m in Sources */, 380 | 29DE451D217DFB5200728149 /* HYExcepitionReportor.m in Sources */, 381 | 29150BF221D32F1400C11A30 /* NSTimer+ExcepitionProtector.m in Sources */, 382 | 29150BEF21D32D6600C11A30 /* NSNotificationCenter+ExcepitionProtector.m in Sources */, 383 | 29DE450D217DC2F500728149 /* NSObject+HYHook.m in Sources */, 384 | 29150BF521D3875100C11A30 /* NSSet+ExcepitionProtector.m in Sources */, 385 | 292B764721CA45F600CE6281 /* Person.m in Sources */, 386 | 29DE44E8217DB73700728149 /* main.m in Sources */, 387 | 29150BEC21D3261900C11A30 /* NSObject+ExcepitionProtectorSet.m in Sources */, 388 | 290FB21821D252660056A25C /* NSObject+KVOCrashProtector.m in Sources */, 389 | 2961120921CB81CC0052469B /* NSMutableArray+ExcepitionProtector.m in Sources */, 390 | 2961120621CB7DDC0052469B /* NSDictionary+ExcepitionProtector.m in Sources */, 391 | 2961120021CB3C6F0052469B /* NSArray+ExcepitionProtector.m in Sources */, 392 | 29DE44DA217DB73500728149 /* AppDelegate.m in Sources */, 393 | 29150BFE21D3A17500C11A30 /* NSMutableString+ExcepitionProtector.m in Sources */, 394 | 29150BF821D3876800C11A30 /* NSMutableSet+ExcepitionProtector.m in Sources */, 395 | 29DE4510217DC32100728149 /* HYExcepitionProtector.m in Sources */, 396 | 290FB20F21D23B350056A25C /* UIView+ExcepitionProtector.m in Sources */, 397 | 290FB21221D240D30056A25C /* HYView.m in Sources */, 398 | ); 399 | runOnlyForDeploymentPostprocessing = 0; 400 | }; 401 | 29DE44E9217DB73700728149 /* Sources */ = { 402 | isa = PBXSourcesBuildPhase; 403 | buildActionMask = 2147483647; 404 | files = ( 405 | 29DE44F2217DB73700728149 /* HYExceptionProtectorTests.m in Sources */, 406 | ); 407 | runOnlyForDeploymentPostprocessing = 0; 408 | }; 409 | 29DE44F4217DB73700728149 /* Sources */ = { 410 | isa = PBXSourcesBuildPhase; 411 | buildActionMask = 2147483647; 412 | files = ( 413 | 29DE44FD217DB73700728149 /* HYExceptionProtectorUITests.m in Sources */, 414 | ); 415 | runOnlyForDeploymentPostprocessing = 0; 416 | }; 417 | /* End PBXSourcesBuildPhase section */ 418 | 419 | /* Begin PBXTargetDependency section */ 420 | 29DE44EF217DB73700728149 /* PBXTargetDependency */ = { 421 | isa = PBXTargetDependency; 422 | target = 29DE44D4217DB73500728149 /* HYExceptionProtector */; 423 | targetProxy = 29DE44EE217DB73700728149 /* PBXContainerItemProxy */; 424 | }; 425 | 29DE44FA217DB73700728149 /* PBXTargetDependency */ = { 426 | isa = PBXTargetDependency; 427 | target = 29DE44D4217DB73500728149 /* HYExceptionProtector */; 428 | targetProxy = 29DE44F9217DB73700728149 /* PBXContainerItemProxy */; 429 | }; 430 | /* End PBXTargetDependency section */ 431 | 432 | /* Begin PBXVariantGroup section */ 433 | 29DE44DE217DB73500728149 /* Main.storyboard */ = { 434 | isa = PBXVariantGroup; 435 | children = ( 436 | 29DE44DF217DB73500728149 /* Base */, 437 | ); 438 | name = Main.storyboard; 439 | sourceTree = ""; 440 | }; 441 | 29DE44E3217DB73700728149 /* LaunchScreen.storyboard */ = { 442 | isa = PBXVariantGroup; 443 | children = ( 444 | 29DE44E4217DB73700728149 /* Base */, 445 | ); 446 | name = LaunchScreen.storyboard; 447 | sourceTree = ""; 448 | }; 449 | /* End PBXVariantGroup section */ 450 | 451 | /* Begin XCBuildConfiguration section */ 452 | 29DE44FF217DB73700728149 /* Debug */ = { 453 | isa = XCBuildConfiguration; 454 | buildSettings = { 455 | ALWAYS_SEARCH_USER_PATHS = NO; 456 | CLANG_ANALYZER_NONNULL = YES; 457 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 458 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 459 | CLANG_CXX_LIBRARY = "libc++"; 460 | CLANG_ENABLE_MODULES = YES; 461 | CLANG_ENABLE_OBJC_ARC = YES; 462 | CLANG_ENABLE_OBJC_WEAK = YES; 463 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 464 | CLANG_WARN_BOOL_CONVERSION = YES; 465 | CLANG_WARN_COMMA = YES; 466 | CLANG_WARN_CONSTANT_CONVERSION = YES; 467 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 468 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 469 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 470 | CLANG_WARN_EMPTY_BODY = YES; 471 | CLANG_WARN_ENUM_CONVERSION = YES; 472 | CLANG_WARN_INFINITE_RECURSION = YES; 473 | CLANG_WARN_INT_CONVERSION = YES; 474 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 475 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 476 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 477 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 478 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 479 | CLANG_WARN_STRICT_PROTOTYPES = YES; 480 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 481 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 482 | CLANG_WARN_UNREACHABLE_CODE = YES; 483 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 484 | CODE_SIGN_IDENTITY = "iPhone Developer"; 485 | COPY_PHASE_STRIP = NO; 486 | DEBUG_INFORMATION_FORMAT = dwarf; 487 | ENABLE_STRICT_OBJC_MSGSEND = YES; 488 | ENABLE_TESTABILITY = YES; 489 | GCC_C_LANGUAGE_STANDARD = gnu11; 490 | GCC_DYNAMIC_NO_PIC = NO; 491 | GCC_NO_COMMON_BLOCKS = YES; 492 | GCC_OPTIMIZATION_LEVEL = 0; 493 | GCC_PREPROCESSOR_DEFINITIONS = ( 494 | "DEBUG=1", 495 | "$(inherited)", 496 | ); 497 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 498 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 499 | GCC_WARN_UNDECLARED_SELECTOR = YES; 500 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 501 | GCC_WARN_UNUSED_FUNCTION = YES; 502 | GCC_WARN_UNUSED_VARIABLE = YES; 503 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 504 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 505 | MTL_FAST_MATH = YES; 506 | ONLY_ACTIVE_ARCH = YES; 507 | SDKROOT = iphoneos; 508 | }; 509 | name = Debug; 510 | }; 511 | 29DE4500217DB73700728149 /* Release */ = { 512 | isa = XCBuildConfiguration; 513 | buildSettings = { 514 | ALWAYS_SEARCH_USER_PATHS = NO; 515 | CLANG_ANALYZER_NONNULL = YES; 516 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 517 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 518 | CLANG_CXX_LIBRARY = "libc++"; 519 | CLANG_ENABLE_MODULES = YES; 520 | CLANG_ENABLE_OBJC_ARC = YES; 521 | CLANG_ENABLE_OBJC_WEAK = YES; 522 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 523 | CLANG_WARN_BOOL_CONVERSION = YES; 524 | CLANG_WARN_COMMA = YES; 525 | CLANG_WARN_CONSTANT_CONVERSION = YES; 526 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 527 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 528 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 529 | CLANG_WARN_EMPTY_BODY = YES; 530 | CLANG_WARN_ENUM_CONVERSION = YES; 531 | CLANG_WARN_INFINITE_RECURSION = YES; 532 | CLANG_WARN_INT_CONVERSION = YES; 533 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 534 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 535 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 536 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 537 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 538 | CLANG_WARN_STRICT_PROTOTYPES = YES; 539 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 540 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 541 | CLANG_WARN_UNREACHABLE_CODE = YES; 542 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 543 | CODE_SIGN_IDENTITY = "iPhone Developer"; 544 | COPY_PHASE_STRIP = NO; 545 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 546 | ENABLE_NS_ASSERTIONS = NO; 547 | ENABLE_STRICT_OBJC_MSGSEND = YES; 548 | GCC_C_LANGUAGE_STANDARD = gnu11; 549 | GCC_NO_COMMON_BLOCKS = YES; 550 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 551 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 552 | GCC_WARN_UNDECLARED_SELECTOR = YES; 553 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 554 | GCC_WARN_UNUSED_FUNCTION = YES; 555 | GCC_WARN_UNUSED_VARIABLE = YES; 556 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 557 | MTL_ENABLE_DEBUG_INFO = NO; 558 | MTL_FAST_MATH = YES; 559 | SDKROOT = iphoneos; 560 | VALIDATE_PRODUCT = YES; 561 | }; 562 | name = Release; 563 | }; 564 | 29DE4502217DB73700728149 /* Debug */ = { 565 | isa = XCBuildConfiguration; 566 | buildSettings = { 567 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 568 | CODE_SIGN_STYLE = Automatic; 569 | DEVELOPMENT_TEAM = BFN377ZHVM; 570 | INFOPLIST_FILE = HYExceptionProtector/Info.plist; 571 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 572 | LD_RUNPATH_SEARCH_PATHS = ( 573 | "$(inherited)", 574 | "@executable_path/Frameworks", 575 | ); 576 | PRODUCT_BUNDLE_IDENTIFIER = com.58jr.HYExceptionProtector; 577 | PRODUCT_NAME = "$(TARGET_NAME)"; 578 | TARGETED_DEVICE_FAMILY = "1,2"; 579 | }; 580 | name = Debug; 581 | }; 582 | 29DE4503217DB73700728149 /* Release */ = { 583 | isa = XCBuildConfiguration; 584 | buildSettings = { 585 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 586 | CODE_SIGN_STYLE = Automatic; 587 | DEVELOPMENT_TEAM = BFN377ZHVM; 588 | INFOPLIST_FILE = HYExceptionProtector/Info.plist; 589 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 590 | LD_RUNPATH_SEARCH_PATHS = ( 591 | "$(inherited)", 592 | "@executable_path/Frameworks", 593 | ); 594 | PRODUCT_BUNDLE_IDENTIFIER = com.58jr.HYExceptionProtector; 595 | PRODUCT_NAME = "$(TARGET_NAME)"; 596 | TARGETED_DEVICE_FAMILY = "1,2"; 597 | }; 598 | name = Release; 599 | }; 600 | 29DE4505217DB73700728149 /* Debug */ = { 601 | isa = XCBuildConfiguration; 602 | buildSettings = { 603 | BUNDLE_LOADER = "$(TEST_HOST)"; 604 | CODE_SIGN_STYLE = Automatic; 605 | DEVELOPMENT_TEAM = BFN377ZHVM; 606 | INFOPLIST_FILE = HYExceptionProtectorTests/Info.plist; 607 | LD_RUNPATH_SEARCH_PATHS = ( 608 | "$(inherited)", 609 | "@executable_path/Frameworks", 610 | "@loader_path/Frameworks", 611 | ); 612 | PRODUCT_BUNDLE_IDENTIFIER = com.58jr.HYExceptionProtectorTests; 613 | PRODUCT_NAME = "$(TARGET_NAME)"; 614 | TARGETED_DEVICE_FAMILY = "1,2"; 615 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HYExceptionProtector.app/HYExceptionProtector"; 616 | }; 617 | name = Debug; 618 | }; 619 | 29DE4506217DB73700728149 /* Release */ = { 620 | isa = XCBuildConfiguration; 621 | buildSettings = { 622 | BUNDLE_LOADER = "$(TEST_HOST)"; 623 | CODE_SIGN_STYLE = Automatic; 624 | DEVELOPMENT_TEAM = BFN377ZHVM; 625 | INFOPLIST_FILE = HYExceptionProtectorTests/Info.plist; 626 | LD_RUNPATH_SEARCH_PATHS = ( 627 | "$(inherited)", 628 | "@executable_path/Frameworks", 629 | "@loader_path/Frameworks", 630 | ); 631 | PRODUCT_BUNDLE_IDENTIFIER = com.58jr.HYExceptionProtectorTests; 632 | PRODUCT_NAME = "$(TARGET_NAME)"; 633 | TARGETED_DEVICE_FAMILY = "1,2"; 634 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HYExceptionProtector.app/HYExceptionProtector"; 635 | }; 636 | name = Release; 637 | }; 638 | 29DE4508217DB73700728149 /* Debug */ = { 639 | isa = XCBuildConfiguration; 640 | buildSettings = { 641 | CODE_SIGN_STYLE = Automatic; 642 | DEVELOPMENT_TEAM = BFN377ZHVM; 643 | INFOPLIST_FILE = HYExceptionProtectorUITests/Info.plist; 644 | LD_RUNPATH_SEARCH_PATHS = ( 645 | "$(inherited)", 646 | "@executable_path/Frameworks", 647 | "@loader_path/Frameworks", 648 | ); 649 | PRODUCT_BUNDLE_IDENTIFIER = com.58jr.HYExceptionProtectorUITests; 650 | PRODUCT_NAME = "$(TARGET_NAME)"; 651 | TARGETED_DEVICE_FAMILY = "1,2"; 652 | TEST_TARGET_NAME = HYExceptionProtector; 653 | }; 654 | name = Debug; 655 | }; 656 | 29DE4509217DB73700728149 /* Release */ = { 657 | isa = XCBuildConfiguration; 658 | buildSettings = { 659 | CODE_SIGN_STYLE = Automatic; 660 | DEVELOPMENT_TEAM = BFN377ZHVM; 661 | INFOPLIST_FILE = HYExceptionProtectorUITests/Info.plist; 662 | LD_RUNPATH_SEARCH_PATHS = ( 663 | "$(inherited)", 664 | "@executable_path/Frameworks", 665 | "@loader_path/Frameworks", 666 | ); 667 | PRODUCT_BUNDLE_IDENTIFIER = com.58jr.HYExceptionProtectorUITests; 668 | PRODUCT_NAME = "$(TARGET_NAME)"; 669 | TARGETED_DEVICE_FAMILY = "1,2"; 670 | TEST_TARGET_NAME = HYExceptionProtector; 671 | }; 672 | name = Release; 673 | }; 674 | /* End XCBuildConfiguration section */ 675 | 676 | /* Begin XCConfigurationList section */ 677 | 29DE44D0217DB73500728149 /* Build configuration list for PBXProject "HYExceptionProtector" */ = { 678 | isa = XCConfigurationList; 679 | buildConfigurations = ( 680 | 29DE44FF217DB73700728149 /* Debug */, 681 | 29DE4500217DB73700728149 /* Release */, 682 | ); 683 | defaultConfigurationIsVisible = 0; 684 | defaultConfigurationName = Release; 685 | }; 686 | 29DE4501217DB73700728149 /* Build configuration list for PBXNativeTarget "HYExceptionProtector" */ = { 687 | isa = XCConfigurationList; 688 | buildConfigurations = ( 689 | 29DE4502217DB73700728149 /* Debug */, 690 | 29DE4503217DB73700728149 /* Release */, 691 | ); 692 | defaultConfigurationIsVisible = 0; 693 | defaultConfigurationName = Release; 694 | }; 695 | 29DE4504217DB73700728149 /* Build configuration list for PBXNativeTarget "HYExceptionProtectorTests" */ = { 696 | isa = XCConfigurationList; 697 | buildConfigurations = ( 698 | 29DE4505217DB73700728149 /* Debug */, 699 | 29DE4506217DB73700728149 /* Release */, 700 | ); 701 | defaultConfigurationIsVisible = 0; 702 | defaultConfigurationName = Release; 703 | }; 704 | 29DE4507217DB73700728149 /* Build configuration list for PBXNativeTarget "HYExceptionProtectorUITests" */ = { 705 | isa = XCConfigurationList; 706 | buildConfigurations = ( 707 | 29DE4508217DB73700728149 /* Debug */, 708 | 29DE4509217DB73700728149 /* Release */, 709 | ); 710 | defaultConfigurationIsVisible = 0; 711 | defaultConfigurationName = Release; 712 | }; 713 | /* End XCConfigurationList section */ 714 | }; 715 | rootObject = 29DE44CD217DB73500728149 /* Project object */; 716 | } 717 | -------------------------------------------------------------------------------- /HYExceptionProtector.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /HYExceptionProtector.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /HYExceptionProtector/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/10/22. 6 | // Copyright © 2018年 张鸿运. 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 | -------------------------------------------------------------------------------- /HYExceptionProtector/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/10/22. 6 | // Copyright © 2018年 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /HYExceptionProtector/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 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /HYExceptionProtector/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /HYExceptionProtector/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 | -------------------------------------------------------------------------------- /HYExceptionProtector/Base.lproj/Main.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 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/HYExcepitionCollector.h: -------------------------------------------------------------------------------- 1 | // 2 | // HYExcepitionCollector.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/10/22. 6 | // Copyright © 2018年 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | void hy_handleErrorWithException(NSException * exception); 13 | 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @interface HYExcepitionCollector : NSObject 18 | 19 | /** 20 | 处理异常信息 21 | 22 | @param exception 异常 23 | */ 24 | + (void)handleErrorWithException:(NSException *)exception; 25 | 26 | @end 27 | 28 | NS_ASSUME_NONNULL_END 29 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/HYExcepitionCollector.m: -------------------------------------------------------------------------------- 1 | // 2 | // HYExcepitionCollector.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/10/22. 6 | // Copyright © 2018年 张鸿运. All rights reserved. 7 | // 8 | 9 | // 打印异常信息的标记 10 | #define ExcepitionHappenedEnd @"================================================================" 11 | #define ExcepitionHappenedStart @"========================⚠️⚠️⚠️⚠️⚠️⚠️⚠️=========================" 12 | // 发生异常的通知 13 | #define ExcepitionHappenedNotification @"ExcepitionHappenedNotification" 14 | 15 | #define kErrorName @"errorName" 16 | #define kErrorReason @"errorReason" 17 | #define kErrorPlace @"errorPlace" 18 | #define kCallStackSymbols @"callStackSymbols" 19 | #define kException @"exception" 20 | 21 | #ifdef DEBUG 22 | #define HYLog(...) NSLog(@"%@",[NSString stringWithFormat:__VA_ARGS__]) 23 | #else 24 | #define HYLog(...) 25 | #endif 26 | 27 | #import "HYExcepitionCollector.h" 28 | 29 | #import 30 | void hy_handleErrorWithException(NSException * exception) 31 | { 32 | [HYExcepitionCollector handleErrorWithException:exception]; 33 | } 34 | 35 | @implementation HYExcepitionCollector 36 | 37 | /** 38 | 简化堆栈信息 39 | 40 | @param callStackSymbols 详细堆栈信息 41 | @return 简化之后的堆栈信息 42 | */ 43 | + (NSString *)getMainCallStackSymbolMessageWithCallStackSymbols:(NSArray *)callStackSymbols 44 | { 45 | // mainCallStackSymbolMsg的格式为 +[类名 方法名] 或者 -[类名 方法名] 46 | __block NSString *mainCallStackSymbolMsg = nil; 47 | 48 | // 匹配出来的格式为 +[类名 方法名] 或者 -[类名 方法名] 49 | NSString *regularExpStr = @"[-\\+]\\[.+\\]"; 50 | 51 | 52 | NSRegularExpression *regularExp = [[NSRegularExpression alloc] initWithPattern:regularExpStr 53 | options:NSRegularExpressionCaseInsensitive 54 | error:nil]; 55 | for (int index = 2; index < callStackSymbols.count; index++) { 56 | 57 | NSString *callStackSymbol = callStackSymbols[index]; 58 | [regularExp enumerateMatchesInString:callStackSymbol 59 | options:NSMatchingReportProgress 60 | range:NSMakeRange(0, callStackSymbol.length) 61 | usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) { 62 | 63 | if (result) { 64 | NSString *tempCallStackSymbolMsg = [callStackSymbol substringWithRange:result.range]; 65 | 66 | NSString *className = [tempCallStackSymbolMsg componentsSeparatedByString:@" "].firstObject; 67 | className = [className componentsSeparatedByString:@"["].lastObject; 68 | 69 | NSBundle *bundle = [NSBundle bundleForClass:NSClassFromString(className)]; 70 | if (![className hasSuffix:@")"] && bundle == [NSBundle mainBundle]) { 71 | mainCallStackSymbolMsg = tempCallStackSymbolMsg; 72 | 73 | } 74 | *stop = YES; 75 | } 76 | 77 | }]; 78 | 79 | if (mainCallStackSymbolMsg.length) break; 80 | 81 | } 82 | 83 | return mainCallStackSymbolMsg; 84 | } 85 | 86 | 87 | + (void)handleErrorWithException:(NSException *)exception 88 | { 89 | // 堆栈数据 90 | NSArray *callStackSymbolsArr = [NSThread callStackSymbols]; 91 | // 获取在哪个类的哪个方法中实例化的数组 字符串格式 -[类名 方法名] 或者 +[类名 方法名] 92 | NSString *mainCallStackSymbolMsg = [self getMainCallStackSymbolMessageWithCallStackSymbols:callStackSymbolsArr]; 93 | 94 | if (mainCallStackSymbolMsg == nil) mainCallStackSymbolMsg = @"崩溃方法定位失败,请您查看函数调用栈来排查错误原因"; 95 | 96 | 97 | NSString *errorName = exception.name; 98 | NSString *errorReason = exception.reason; 99 | // errorReason 可能为 -[__NSCFConstantString avoidCrashCharacterAtIndex:]: Range or index out of bounds 100 | errorReason = [errorReason stringByReplacingOccurrencesOfString:@"avoidCrash" withString:@""]; 101 | 102 | // 拼接错误信息 103 | NSString *errorPlace = [NSString stringWithFormat:@"Error Place:%@",mainCallStackSymbolMsg]; 104 | NSString *logErrorMessage = [NSString stringWithFormat:@"\n\n%@\n\n%@\n%@\n%@\n",ExcepitionHappenedStart, errorName, errorReason, errorPlace]; 105 | logErrorMessage = [NSString stringWithFormat:@"%@\n\n%@\n\n",logErrorMessage,ExcepitionHappenedEnd]; 106 | 107 | HYLog(@"%@",logErrorMessage); 108 | 109 | #if DEBUG 110 | UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:errorName 111 | message:errorReason 112 | preferredStyle:UIAlertControllerStyleAlert]; 113 | 114 | [alertVC addAction:[UIAlertAction actionWithTitle:@"好" 115 | style:UIAlertActionStyleDefault 116 | handler:nil]]; 117 | [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertVC 118 | animated:YES 119 | completion:nil]; 120 | #endif 121 | 122 | 123 | NSDictionary *errorInfoDic = @{ 124 | kErrorName : errorName, 125 | kErrorReason : errorReason, 126 | kErrorPlace : errorPlace, 127 | kException : exception, 128 | kCallStackSymbols : callStackSymbolsArr 129 | }; 130 | 131 | // 将错误信息放在字典里,用通知的形式发送出去 132 | dispatch_async(dispatch_get_main_queue(), ^{ 133 | 134 | [[NSNotificationCenter defaultCenter] postNotificationName:ExcepitionHappenedNotification object:nil userInfo:errorInfoDic]; 135 | 136 | }); 137 | } 138 | 139 | @end 140 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/HYExcepitionProtectedProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // HYExcepitionProtectedProtocol.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/10/22. 6 | // Copyright © 2018年 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @protocol HYExcepitionProtectedProtocol 14 | 15 | @required 16 | /** 17 | 交换方法 18 | */ 19 | + (void)hy_swizzleMethod; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/HYExcepitionProtector.h: -------------------------------------------------------------------------------- 1 | // 2 | // HYExcepitionProtector.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/10/22. 6 | // Copyright © 2018年 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | #ifdef __cplusplus 14 | #define HY_EXTERN extern "C" __attribute__((visibility ("default"))) 15 | #else 16 | #define HY_EXTERN extern __attribute__((visibility ("default"))) 17 | #endif 18 | 19 | 20 | typedef NSString * HYExcepitionProtectorStrategy; 21 | /** 保护未实现方法的崩溃 */ 22 | HY_EXTERN HYExcepitionProtectorStrategy const HYExcepitionProtectorStrategyUnrecognizedSelector; 23 | /** 保护数组崩溃 */ 24 | HY_EXTERN HYExcepitionProtectorStrategy const HYExcepitionProtectorStrategyArray; 25 | /** 保护字典崩溃 */ 26 | HY_EXTERN HYExcepitionProtectorStrategy const HYExcepitionProtectorStrategyDictionary; 27 | /** 保护NSTimer崩溃 */ 28 | HY_EXTERN HYExcepitionProtectorStrategy const HYExcepitionProtectorStrategyNSTimer; 29 | /** 保护字符串崩溃 */ 30 | HY_EXTERN HYExcepitionProtectorStrategy const HYExcepitionProtectorStrategyString; 31 | /** 保护所有类型崩溃 */ 32 | HY_EXTERN HYExcepitionProtectorStrategy const HYExcepitionProtectorStrategyAll; 33 | /** 开发过程中,使在子线程中刷新UI的操作崩溃,线上保护崩溃 */ 34 | HY_EXTERN HYExcepitionProtectorStrategy const HYExcepitionProtectorStrategyUIView; 35 | 36 | 37 | @interface HYExcepitionProtector : NSObject 38 | 39 | /** 当有崩溃操作时,是否显示警告(Alert提示) */ 40 | @property (nonatomic, assign) BOOL isShowWarning; 41 | 42 | + (instancetype)shareInstance; 43 | 44 | /** 45 | 设置单种崩溃保护类型 46 | 47 | @param type 需要保护的崩溃类型 48 | */ 49 | - (void)configProtectType:(HYExcepitionProtectorStrategy)type; 50 | 51 | /** 52 | 设置多种崩溃保护类型 53 | 54 | @param types 需要保护的崩溃类型数组 55 | */ 56 | - (void)configProtectTypes:(NSArray *)types; 57 | 58 | /** 59 | 保护所有崩溃类型 60 | 61 | @mark 所有崩溃类型指已实现的上述类型,上述类型之外的崩溃不在保护之内; 62 | */ 63 | - (void)configAllProtectTypes; 64 | 65 | /** 66 | 开启保护 67 | */ 68 | - (void)startProtection; 69 | 70 | @end 71 | 72 | 73 | 74 | NS_ASSUME_NONNULL_END 75 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/HYExcepitionProtector.m: -------------------------------------------------------------------------------- 1 | // 2 | // HYExcepitionProtector.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/10/22. 6 | // Copyright © 2018年 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "HYExcepitionProtector.h" 10 | #import "HYExcepitionProtectedProtocol.h" 11 | #import 12 | 13 | 14 | HYExcepitionProtectorStrategy const HYExcepitionProtectorStrategyUnrecognizedSelector = @"NSObject"; 15 | HYExcepitionProtectorStrategy const HYExcepitionProtectorStrategyArray = @"NSArray"; 16 | HYExcepitionProtectorStrategy const HYExcepitionProtectorStrategyDictionary = @"NSDictionary"; 17 | HYExcepitionProtectorStrategy const HYExcepitionProtectorStrategyUIView = @"UIView"; 18 | HYExcepitionProtectorStrategy const HYExcepitionProtectorStrategyNSTimer = @"NSTimer"; 19 | HYExcepitionProtectorStrategy const HYExcepitionProtectorStrategyString = @"NSString"; 20 | HYExcepitionProtectorStrategy const HYExcepitionProtectorStrategyAll = @"All"; 21 | 22 | 23 | @interface HYExcepitionProtector () 24 | { 25 | // 保护交换方法的安全 26 | dispatch_semaphore_t _swizzleLock; 27 | } 28 | @property (nonatomic, copy) NSArray *protectedStrategysArray; 29 | @property (nonatomic, strong) NSMutableArray *protectedClassArray; 30 | 31 | @end 32 | 33 | @implementation HYExcepitionProtector 34 | 35 | - (instancetype)init 36 | { 37 | if (self = [super init]) { 38 | 39 | _swizzleLock = dispatch_semaphore_create(1); 40 | 41 | } 42 | return self; 43 | } 44 | 45 | #pragma mark - Public Method 46 | 47 | + (instancetype)shareInstance 48 | { 49 | static HYExcepitionProtector *instance; 50 | static dispatch_once_t onceToken; 51 | 52 | dispatch_once(&onceToken, ^{ 53 | 54 | instance = [[HYExcepitionProtector alloc] init]; 55 | 56 | }); 57 | 58 | return instance; 59 | } 60 | 61 | - (void)configProtectType:(HYExcepitionProtectorStrategy)type 62 | { 63 | if ([type isEqualToString:HYExcepitionProtectorStrategyAll]) { 64 | 65 | [self configAllProtectTypes]; 66 | return; 67 | 68 | } 69 | 70 | if ([type isEqualToString:HYExcepitionProtectorStrategyArray]) { 71 | 72 | Class cls = NSClassFromString(@"NSArray"); 73 | [self addClassToArray:cls]; 74 | 75 | 76 | Class clsM = NSClassFromString(@"NSMutableArray"); 77 | [self addClassToArray:clsM]; 78 | 79 | return; 80 | 81 | } 82 | 83 | if ([type isEqualToString:HYExcepitionProtectorStrategyDictionary]) { 84 | 85 | Class cls = NSClassFromString(@"NSDictionary"); 86 | [self addClassToArray:cls]; 87 | 88 | 89 | Class clsM = NSClassFromString(@"NSMutableDictionary"); 90 | [self addClassToArray:clsM]; 91 | 92 | return; 93 | 94 | } 95 | 96 | if ([type isEqualToString:HYExcepitionProtectorStrategyString]) { 97 | 98 | Class cls = NSClassFromString(@"NSString"); 99 | [self addClassToArray:cls]; 100 | 101 | 102 | Class clsM = NSClassFromString(@"NSMutableString"); 103 | [self addClassToArray:clsM]; 104 | 105 | return; 106 | 107 | } 108 | 109 | Class cls = NSClassFromString(type); 110 | [self addClassToArray:cls]; 111 | 112 | } 113 | 114 | 115 | - (void)configProtectTypes:(NSArray *)types 116 | { 117 | [types enumerateObjectsUsingBlock:^(HYExcepitionProtectorStrategy _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 118 | 119 | if ([obj isEqualToString:HYExcepitionProtectorStrategyAll]) { 120 | 121 | * stop = YES; 122 | [self configAllProtectTypes]; 123 | 124 | } 125 | 126 | [self configProtectType:obj]; 127 | 128 | }]; 129 | } 130 | 131 | - (void)configAllProtectTypes 132 | { 133 | __weak typeof(self) weakSelf = self; 134 | [self.protectedStrategysArray enumerateObjectsUsingBlock:^(HYExcepitionProtectorStrategy _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 135 | 136 | [weakSelf configProtectType:obj]; 137 | 138 | }]; 139 | } 140 | 141 | - (void)startProtection 142 | { 143 | dispatch_semaphore_wait(_swizzleLock, DISPATCH_TIME_FOREVER); 144 | 145 | [self.protectedClassArray enumerateObjectsUsingBlock:^(Class _Nonnull cls, NSUInteger idx, BOOL * _Nonnull stop) { 146 | 147 | [cls performSelector:@selector(hy_swizzleMethod)]; 148 | 149 | }]; 150 | 151 | dispatch_semaphore_signal(_swizzleLock); 152 | 153 | } 154 | 155 | #pragma mark - Private Method 156 | 157 | - (void)addClassToArray:(Class)cls 158 | { 159 | if ([cls conformsToProtocol:@protocol(HYExcepitionProtectedProtocol)]) { 160 | 161 | [self.protectedClassArray addObject:cls]; 162 | 163 | }; 164 | } 165 | 166 | 167 | #pragma mark - getter / setter 168 | 169 | - (NSArray *)protectedStrategysArray 170 | { 171 | if (!_protectedStrategysArray) { 172 | 173 | _protectedStrategysArray = @[HYExcepitionProtectorStrategyArray, 174 | HYExcepitionProtectorStrategyDictionary, 175 | HYExcepitionProtectorStrategyUnrecognizedSelector, 176 | HYExcepitionProtectorStrategyUIView, 177 | HYExcepitionProtectorStrategyNSTimer, 178 | HYExcepitionProtectorStrategyString]; 179 | } 180 | 181 | return _protectedStrategysArray; 182 | } 183 | 184 | - (NSMutableArray *)protectedClassArray 185 | { 186 | if (!_protectedClassArray) { 187 | 188 | _protectedClassArray = [[NSMutableArray alloc] init]; 189 | } 190 | 191 | return _protectedClassArray; 192 | } 193 | @end 194 | 195 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/HYExcepitionReportor.h: -------------------------------------------------------------------------------- 1 | // 2 | // HYExcepitionReportor.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/10/22. 6 | // Copyright © 2018年 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface HYExcepitionReportor : NSObject 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/HYExcepitionReportor.m: -------------------------------------------------------------------------------- 1 | // 2 | // HYExcepitionReportor.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/10/22. 6 | // Copyright © 2018年 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "HYExcepitionReportor.h" 10 | 11 | @implementation HYExcepitionReportor 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSArray+ExcepitionProtector.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+ExcepitionProtector.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/20. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HYExcepitionProtectedProtocol.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NSArray (ExcepitionProtector) 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSArray+ExcepitionProtector.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+ExcepitionProtector.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/20. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "NSArray+ExcepitionProtector.h" 10 | #import "HYExcepitionCollector.h" 11 | #import 12 | #import "NSObject+HYHook.h" 13 | 14 | @implementation NSArray (ExcepitionProtector) 15 | 16 | + (void)hy_swizzleMethod 17 | { 18 | static dispatch_once_t onceToken; 19 | dispatch_once(&onceToken, ^{ 20 | 21 | //================= 22 | // 类方法交换 23 | //================= 24 | [NSArray hy_swizzleClassMethodImplementation:@selector(arrayWithObject:) withSEL:@selector(safeArrayWithObject:)]; 25 | [NSArray hy_swizzleClassMethodImplementation:@selector(arrayWithObjects:count:) withSEL:@selector(safeArrayWithObjects:count:)]; 26 | 27 | //=================== 28 | // 实例方法交换 29 | //=================== 30 | // __NSArray0 31 | Class clsArray0 = objc_getClass("__NSArray0"); 32 | hy_swizzleInstanceMethodImplementation(clsArray0, @selector(objectAtIndex:), @selector(safeObjectAtIndex:)); 33 | hy_swizzleInstanceMethodImplementation(clsArray0, @selector(subarrayWithRange:), @selector(safeSubarrayWithRange:)); 34 | hy_swizzleInstanceMethodImplementation(clsArray0, @selector(objectAtIndexedSubscript:), @selector(safeObjectAtIndexedSubscript:)); 35 | 36 | // __NSArrayI 37 | Class clsArray1 = objc_getClass("__NSArrayI"); 38 | hy_swizzleInstanceMethodImplementation(clsArray1, @selector(objectAtIndex:), @selector(safeObjectAtIndex:)); 39 | hy_swizzleInstanceMethodImplementation(clsArray1, @selector(subarrayWithRange:), @selector(safeSubarrayWithRange:)); 40 | hy_swizzleInstanceMethodImplementation(clsArray1, @selector(objectAtIndexedSubscript:), @selector(safeObjectAtIndexedSubscript:)); 41 | 42 | // __NSArrayI_Transfer 43 | Class clsArrayIT = objc_getClass("__NSArrayI_Transfer"); 44 | hy_swizzleInstanceMethodImplementation(clsArrayIT, @selector(objectAtIndex:), @selector(safeObjectAtIndex:)); 45 | hy_swizzleInstanceMethodImplementation(clsArrayIT, @selector(subarrayWithRange:), @selector(safeSubarrayWithRange:)); 46 | hy_swizzleInstanceMethodImplementation(clsArrayIT, @selector(objectAtIndexedSubscript:), @selector(safeObjectAtIndexedSubscript:)); 47 | 48 | 49 | // above iOS10 __NSSingleObjectArrayI 50 | Class clsArraySI = objc_getClass("__NSSingleObjectArrayI"); 51 | hy_swizzleInstanceMethodImplementation(clsArraySI, @selector(objectAtIndex:), @selector(safeObjectAtIndex:)); 52 | hy_swizzleInstanceMethodImplementation(clsArraySI, @selector(subarrayWithRange:), @selector(safeSubarrayWithRange:)); 53 | hy_swizzleInstanceMethodImplementation(clsArraySI, @selector(objectAtIndexedSubscript:), @selector(safeObjectAtIndexedSubscript:)); 54 | 55 | // __NSFrozenArrayM 56 | Class clsArrayM = objc_getClass("__NSFrozenArrayM"); 57 | hy_swizzleInstanceMethodImplementation(clsArrayM, @selector(objectAtIndex:), @selector(safeObjectAtIndex:)); 58 | hy_swizzleInstanceMethodImplementation(clsArrayM, @selector(subarrayWithRange:), @selector(safeSubarrayWithRange:)); 59 | hy_swizzleInstanceMethodImplementation(clsArrayM, @selector(objectAtIndexedSubscript:), @selector(safeObjectAtIndexedSubscript:)); 60 | 61 | // __NSArrayReversed 62 | Class clsArrayR = objc_getClass("__NSArrayReversed"); 63 | hy_swizzleInstanceMethodImplementation(clsArrayR, @selector(objectAtIndex:), @selector(safeObjectAtIndex:)); 64 | hy_swizzleInstanceMethodImplementation(clsArrayR, @selector(subarrayWithRange:), @selector(safeSubarrayWithRange:)); 65 | hy_swizzleInstanceMethodImplementation(clsArrayR, @selector(objectAtIndexedSubscript:), @selector(safeObjectAtIndexedSubscript:)); 66 | 67 | }); 68 | 69 | } 70 | 71 | + (instancetype)safeArrayWithObject:(id)anObject 72 | { 73 | id array = nil; 74 | 75 | @try { 76 | 77 | array = [self safeArrayWithObject:anObject]; 78 | 79 | } 80 | @catch (NSException *exception) { 81 | 82 | hy_handleErrorWithException(exception); 83 | 84 | } 85 | @finally { 86 | 87 | return array; 88 | } 89 | } 90 | 91 | - (id)safeObjectAtIndex:(NSUInteger)index 92 | { 93 | id object = nil; 94 | 95 | @try { 96 | 97 | object = [self safeObjectAtIndex:index]; 98 | 99 | } 100 | @catch (NSException *exception) { 101 | 102 | hy_handleErrorWithException(exception); 103 | 104 | } 105 | @finally { 106 | 107 | return object; 108 | } 109 | } 110 | 111 | - (id)safeObjectAtIndexedSubscript:(NSInteger)index 112 | { 113 | id object = nil; 114 | 115 | @try { 116 | 117 | object = [self safeObjectAtIndexedSubscript:index]; 118 | 119 | } 120 | @catch (NSException *exception) { 121 | 122 | hy_handleErrorWithException(exception); 123 | 124 | } 125 | @finally { 126 | 127 | return object; 128 | } 129 | } 130 | 131 | - (NSArray *)safeSubarrayWithRange:(NSRange)range 132 | { 133 | NSArray *array = nil; 134 | 135 | @try { 136 | 137 | array = [self safeSubarrayWithRange:range]; 138 | 139 | } @catch (NSException *exception) { 140 | 141 | array = [self safeSubarrayWithRange:NSMakeRange(range.location, self.count - range.location)]; 142 | 143 | // 收集错误信息 144 | hy_handleErrorWithException(exception); 145 | 146 | } @finally { 147 | 148 | return array; 149 | } 150 | } 151 | 152 | + (instancetype)safeArrayWithObjects:(const id [])objects count:(NSUInteger)cnt 153 | { 154 | id array = nil; 155 | 156 | @try { 157 | 158 | array = [self safeArrayWithObjects:objects count:cnt]; 159 | 160 | } 161 | @catch (NSException *exception) { 162 | 163 | // 把为nil的数据去掉,然后初始化数组 164 | NSInteger newObjsIndex = 0; 165 | id _Nonnull __unsafe_unretained newObjects[cnt]; 166 | 167 | for (int i = 0; i < cnt; i++) { 168 | 169 | if (objects[i] != nil) { 170 | 171 | newObjects[newObjsIndex] = objects[i]; 172 | newObjsIndex++; 173 | } 174 | } 175 | 176 | array = [self safeArrayWithObjects:newObjects count:newObjsIndex]; 177 | 178 | // 收集错误信息 179 | hy_handleErrorWithException(exception); 180 | 181 | } 182 | @finally { 183 | 184 | return array; 185 | } 186 | } 187 | 188 | @end 189 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSDictionary+ExcepitionProtector.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+ExcepitionProtector.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/20. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HYExcepitionProtectedProtocol.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NSDictionary (ExcepitionProtector) 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSDictionary+ExcepitionProtector.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+ExcepitionProtector.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/20. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "NSDictionary+ExcepitionProtector.h" 10 | #import "HYExcepitionCollector.h" 11 | #import 12 | #import "NSObject+HYHook.h" 13 | 14 | @implementation NSDictionary (ExcepitionProtector) 15 | 16 | + (void)hy_swizzleMethod 17 | { 18 | 19 | static dispatch_once_t onceToken; 20 | dispatch_once(&onceToken, ^{ 21 | 22 | hy_swizzleClassMethodImplementation([self class], @selector(dictionaryWithObject:forKey:), @selector(safeDictionaryWithObject:forKey:)); 23 | hy_swizzleClassMethodImplementation([self class], @selector(dictionaryWithObjects:forKeys:count:), @selector(safeDictionaryWithObjects:forKeys:count:)); 24 | 25 | 26 | Class clsSingleEntryDictionaryI = objc_getClass("__NSSingleEntryDictionaryI"); 27 | hy_swizzleInstanceMethodImplementation(clsSingleEntryDictionaryI, @selector(initWithObjectsAndKeys:), @selector(safeInitWithObjectsAndKeys:)); 28 | hy_swizzleInstanceMethodImplementation(clsSingleEntryDictionaryI, @selector(initWithObjects:forKeys:), @selector(safeInitWithObjects:forKeys:)); 29 | 30 | }); 31 | 32 | } 33 | 34 | - (instancetype)safeInitWithObjects:(NSArray *)objects forKeys:(NSArray> *)keys 35 | { 36 | id dictionary = nil; 37 | 38 | @try { 39 | 40 | dictionary = [self safeInitWithObjects:objects forKeys:keys]; 41 | 42 | } @catch (NSException *exception) { 43 | 44 | if (objects && keys) { 45 | 46 | NSInteger count = objects.count > keys.count ? keys.count : objects.count; 47 | 48 | NSMutableArray *newObjects = [NSMutableArray arrayWithCapacity:count]; 49 | NSMutableArray *newkeys = [NSMutableArray arrayWithCapacity:count]; 50 | 51 | for (NSInteger i = 0; i < count; i++) { 52 | 53 | if (objects[i] && keys[i]) { 54 | 55 | newObjects[i] = objects[i]; 56 | newkeys[i] = keys[i]; 57 | 58 | } 59 | } 60 | 61 | dictionary = [self safeInitWithObjects:newObjects forKeys:newkeys]; 62 | 63 | } 64 | // 收集错误信息 65 | hy_handleErrorWithException(exception); 66 | 67 | } @finally { 68 | 69 | return dictionary; 70 | 71 | } 72 | } 73 | 74 | - (instancetype)safeInitWithObjectsAndKeys:(id)firstObject, ... 75 | { 76 | NSMutableArray *objects = [[NSMutableArray alloc] init]; 77 | va_list list; 78 | va_start(list, firstObject); 79 | [objects addObject:firstObject]; 80 | id arg = nil; 81 | while ((arg = va_arg(list, id))) { 82 | 83 | [objects addObject:arg]; 84 | } 85 | va_end(list); 86 | 87 | if (objects.count % 2 != 0) { 88 | NSException *exception = [[NSException alloc] initWithName:NSInvalidArgumentException 89 | reason:@"-[__NSPlaceholderDictionary initWithObjectsAndKeys:]: second object of each pair must be non-nil" 90 | userInfo:nil]; 91 | // 收集错误信息 92 | hy_handleErrorWithException(exception); 93 | 94 | } 95 | 96 | // 对参数处理,丢弃最后一个参数,然后组成一个字典 97 | NSInteger index = objects.count % 2 == 0 ? objects.count : objects.count - 1; 98 | NSMutableArray *keys = [NSMutableArray arrayWithCapacity:objects.count / 2]; 99 | NSMutableArray *values = [NSMutableArray arrayWithCapacity:objects.count / 2]; 100 | for (NSInteger i = 0; i < index; i++) { 101 | 102 | if (i % 2 == 0) { 103 | 104 | [values addObject:objects[i]]; 105 | 106 | } else { 107 | 108 | [keys addObject:objects[i]]; 109 | 110 | } 111 | } 112 | 113 | return [[NSDictionary alloc] initWithObjects:[values copy] forKeys:[keys copy]]; 114 | 115 | } 116 | 117 | + (instancetype)safeDictionaryWithObject:(id)object forKey:(id)key 118 | { 119 | id dictionary = nil; 120 | 121 | @try { 122 | 123 | dictionary = [self safeDictionaryWithObject:object forKey:key]; 124 | 125 | } @catch (NSException *exception) { 126 | 127 | // 收集错误信息 128 | hy_handleErrorWithException(exception); 129 | 130 | } @finally { 131 | 132 | return dictionary; 133 | } 134 | 135 | } 136 | 137 | + (instancetype)safeDictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(NSUInteger)cnt 138 | { 139 | id dictionary = nil; 140 | 141 | @try { 142 | 143 | dictionary = [self safeDictionaryWithObjects:objects forKeys:keys count:cnt]; 144 | 145 | } 146 | @catch (NSException *exception) { 147 | 148 | // 处理错误的数据,然后重新初始化一个字典 149 | NSUInteger index = 0; 150 | id _Nonnull __unsafe_unretained newObjects[cnt]; 151 | id _Nonnull __unsafe_unretained newkeys[cnt]; 152 | 153 | for (int i = 0; i < cnt; i++) { 154 | 155 | if (objects[i] && keys[i]) { 156 | 157 | newObjects[index] = objects[i]; 158 | newkeys[index] = keys[i]; 159 | index++; 160 | 161 | } 162 | 163 | } 164 | dictionary = [self safeDictionaryWithObjects:newObjects forKeys:newkeys count:index]; 165 | 166 | // 收集错误信息 167 | hy_handleErrorWithException(exception); 168 | 169 | } 170 | @finally { 171 | 172 | return dictionary; 173 | 174 | } 175 | } 176 | 177 | 178 | @end 179 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSMutableArray+ExcepitionProtector.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableArray+ExcepitionProtector.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/20. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HYExcepitionProtectedProtocol.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NSMutableArray (ExcepitionProtector) 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSMutableArray+ExcepitionProtector.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableArray+ExcepitionProtector.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/20. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "NSMutableArray+ExcepitionProtector.h" 10 | #import "HYExcepitionCollector.h" 11 | #import 12 | #import "NSObject+HYHook.h" 13 | 14 | @implementation NSMutableArray (ExcepitionProtector) 15 | 16 | + (void)hy_swizzleMethod 17 | { 18 | Class clsArrayM = objc_getClass("__NSArrayM"); 19 | hy_swizzleInstanceMethodImplementation(clsArrayM, @selector(addObject:),@selector(safeAddObject:)); 20 | hy_swizzleInstanceMethodImplementation(clsArrayM, @selector(objectAtIndex:), @selector(safeObjectAtIndex:)); 21 | hy_swizzleInstanceMethodImplementation(clsArrayM, @selector(objectAtIndexedSubscript:), @selector(safeObjectAtIndexedSubscript:)); 22 | hy_swizzleInstanceMethodImplementation(clsArrayM, @selector(insertObject:atIndex:), @selector(safeInsertObject:atIndex:)); 23 | hy_swizzleInstanceMethodImplementation(clsArrayM, @selector(removeObjectAtIndex:), @selector(safeRemoveObjectAtIndex:)); 24 | hy_swizzleInstanceMethodImplementation(clsArrayM, @selector(replaceObjectAtIndex:withObject:), @selector(safeReplaceObjectAtIndex:withObject:)); 25 | hy_swizzleInstanceMethodImplementation(clsArrayM, @selector(removeObjectsInRange:), @selector(safeRemoveObjectsInRange:)); 26 | hy_swizzleInstanceMethodImplementation(clsArrayM, @selector(subarrayWithRange:), @selector(safeSubarrayWithRange:)); 27 | 28 | } 29 | 30 | - (void)safeAddObject:(id)anObject 31 | { 32 | @try { 33 | 34 | [self safeAddObject:anObject]; 35 | 36 | } @catch (NSException *exception) { 37 | 38 | hy_handleErrorWithException(exception); 39 | 40 | } @finally { 41 | 42 | } 43 | 44 | } 45 | 46 | - (id)safeObjectAtIndex:(NSUInteger)index 47 | { 48 | id object = nil; 49 | 50 | @try { 51 | 52 | object = [self safeObjectAtIndex:index]; 53 | 54 | } @catch (NSException *exception) { 55 | 56 | object = [self lastObject]; 57 | 58 | hy_handleErrorWithException(exception); 59 | 60 | } @finally { 61 | 62 | return object; 63 | } 64 | 65 | } 66 | 67 | - (id)safeObjectAtIndexedSubscript:(NSInteger)index 68 | { 69 | id object = nil; 70 | 71 | @try { 72 | 73 | object = [self safeObjectAtIndexedSubscript:index]; 74 | 75 | } @catch (NSException *exception) { 76 | 77 | object = [self lastObject]; 78 | 79 | hy_handleErrorWithException(exception); 80 | 81 | } @finally { 82 | 83 | return object; 84 | 85 | } 86 | 87 | } 88 | 89 | - (void)safeInsertObject:(id)anObject atIndex:(NSUInteger)index 90 | { 91 | @try { 92 | 93 | [self safeInsertObject:anObject atIndex:index]; 94 | 95 | } @catch (NSException *exception) { 96 | 97 | hy_handleErrorWithException(exception); 98 | 99 | } @finally { 100 | 101 | } 102 | 103 | } 104 | 105 | - (void)safeRemoveObjectAtIndex:(NSUInteger)index 106 | { 107 | @try { 108 | 109 | [self safeRemoveObjectAtIndex:index]; 110 | 111 | } @catch (NSException *exception) { 112 | 113 | hy_handleErrorWithException(exception); 114 | 115 | } @finally { 116 | 117 | } 118 | 119 | } 120 | 121 | 122 | - (void)safeReplaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject 123 | { 124 | @try { 125 | 126 | [self safeReplaceObjectAtIndex:index withObject:anObject]; 127 | 128 | } @catch (NSException *exception) { 129 | 130 | hy_handleErrorWithException(exception); 131 | 132 | } @finally { 133 | 134 | } 135 | 136 | } 137 | 138 | - (void)safeRemoveObjectsInRange:(NSRange)range 139 | { 140 | @try { 141 | 142 | [self safeRemoveObjectsInRange:range]; 143 | 144 | } @catch (NSException *exception) { 145 | 146 | hy_handleErrorWithException(exception); 147 | 148 | } @finally { 149 | 150 | } 151 | 152 | } 153 | 154 | - (NSArray *)safeSubarrayWithRange:(NSRange)range 155 | { 156 | NSArray *array = nil; 157 | 158 | @try { 159 | 160 | array = [self safeSubarrayWithRange:range]; 161 | 162 | } @catch (NSException *exception) { 163 | 164 | array = [self safeSubarrayWithRange:NSMakeRange(range.location, self.count - range.location)]; 165 | 166 | // 收集错误信息 167 | hy_handleErrorWithException(exception); 168 | 169 | } @finally { 170 | 171 | return array; 172 | } 173 | } 174 | 175 | @end 176 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSMutableDictionary+ExcepitionProtector.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableDictionary+ExcepitionProtector.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/25. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HYExcepitionProtectedProtocol.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NSMutableDictionary (ExcepitionProtector) 15 | 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSMutableDictionary+ExcepitionProtector.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableDictionary+ExcepitionProtector.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/25. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "NSMutableDictionary+ExcepitionProtector.h" 10 | #import "HYExcepitionCollector.h" 11 | #import 12 | #import "NSObject+HYHook.h" 13 | 14 | @implementation NSMutableDictionary (ExcepitionProtector) 15 | 16 | + (void)hy_swizzleMethod 17 | { 18 | static dispatch_once_t onceToken; 19 | dispatch_once(&onceToken, ^{ 20 | 21 | Class clsDictinaryM = objc_getClass("__NSDictionaryM"); 22 | hy_swizzleInstanceMethodImplementation(clsDictinaryM, @selector(setObject:forKey:), @selector(safeSetObject:forKey:)); 23 | hy_swizzleInstanceMethodImplementation(clsDictinaryM, @selector(setObject:forKeyedSubscript:), @selector(safeSetObject:forKeyedSubscript:)); 24 | hy_swizzleInstanceMethodImplementation(clsDictinaryM, @selector(removeObjectForKey:), @selector(safeRemoveObjectForKey:)); 25 | 26 | }); 27 | } 28 | 29 | 30 | - (void)safeSetObject:(id)anObject forKey:(id)aKey 31 | { 32 | 33 | @try { 34 | 35 | [self safeSetObject:anObject forKey:aKey]; 36 | 37 | } 38 | @catch (NSException *exception) { 39 | 40 | // 收集错误信息 41 | hy_handleErrorWithException(exception); 42 | 43 | } 44 | @finally { 45 | 46 | } 47 | } 48 | 49 | 50 | - (void)safeSetObject:(id)obj forKeyedSubscript:(id)key 51 | { 52 | @try { 53 | 54 | [self safeSetObject:obj forKeyedSubscript:key]; 55 | 56 | } 57 | @catch (NSException *exception) { 58 | 59 | // 收集错误信息 60 | hy_handleErrorWithException(exception); 61 | 62 | } 63 | @finally { 64 | 65 | } 66 | } 67 | 68 | 69 | - (void)safeRemoveObjectForKey:(id)aKey 70 | { 71 | 72 | @try { 73 | 74 | [self safeRemoveObjectForKey:aKey]; 75 | 76 | } 77 | @catch (NSException *exception) { 78 | 79 | // 收集错误信息 80 | hy_handleErrorWithException(exception); 81 | 82 | } 83 | @finally { 84 | 85 | } 86 | } 87 | 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSMutableSet+ExcepitionProtector.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableSet+ExcepitionProtector.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/26. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HYExcepitionProtectedProtocol.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NSMutableSet (ExcepitionProtector) 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSMutableSet+ExcepitionProtector.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableSet+ExcepitionProtector.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/26. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "NSMutableSet+ExcepitionProtector.h" 10 | #import "NSObject+HYHook.h" 11 | #import 12 | #import "HYExcepitionCollector.h" 13 | 14 | 15 | @implementation NSMutableSet (ExcepitionProtector) 16 | 17 | 18 | + (void)hy_swizzleMethod 19 | { 20 | static dispatch_once_t onceToken; 21 | dispatch_once(&onceToken, ^{ 22 | 23 | Class cls = objc_getClass("NSMutableSet"); 24 | hy_swizzleInstanceMethodImplementation(cls, @selector(addObject:), @selector(safeddObject:)); 25 | hy_swizzleInstanceMethodImplementation(cls, @selector(removeObject:), @selector(safeRemoveObject:)); 26 | 27 | }); 28 | } 29 | 30 | 31 | - (void)safeddObject:(id)object 32 | { 33 | @try { 34 | 35 | [self safeddObject:object]; 36 | 37 | } @catch (NSException *exception) { 38 | 39 | hy_handleErrorWithException(exception); 40 | 41 | } @finally { 42 | 43 | } 44 | } 45 | 46 | - (void)safeRemoveObject:(id)object 47 | { 48 | @try { 49 | 50 | [self safeRemoveObject:object]; 51 | 52 | } @catch (NSException *exception) { 53 | 54 | hy_handleErrorWithException(exception); 55 | 56 | } @finally { 57 | 58 | } 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSMutableString+ExcepitionProtector.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableString+ExcepitionProtector.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/26. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HYExcepitionProtectedProtocol.h" 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NSMutableString (ExcepitionProtector) 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSMutableString+ExcepitionProtector.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableString+ExcepitionProtector.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/26. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "NSMutableString+ExcepitionProtector.h" 10 | #import "NSObject+HYHook.h" 11 | #import 12 | #import "HYExcepitionCollector.h" 13 | 14 | 15 | @implementation NSMutableString (ExcepitionProtector) 16 | 17 | + (void)hy_swizzleMethod 18 | { 19 | static dispatch_once_t onceToken; 20 | dispatch_once(&onceToken, ^{ 21 | 22 | Class cls = objc_getClass("__NSCFString"); 23 | hy_swizzleInstanceMethodImplementation(cls, @selector(appendString:), @selector(safeAppendString:)); 24 | hy_swizzleInstanceMethodImplementation(cls, @selector(insertString:atIndex:), @selector(safeInsertString:atIndex:)); 25 | hy_swizzleInstanceMethodImplementation(cls, @selector(deleteCharactersInRange:), @selector(safeDeleteCharactersInRange:)); 26 | hy_swizzleInstanceMethodImplementation(cls, @selector(substringFromIndex:), @selector(safeSubstringFromIndex:)); 27 | hy_swizzleInstanceMethodImplementation(cls, @selector(substringToIndex:), @selector(safeSubstringToIndex:)); 28 | hy_swizzleInstanceMethodImplementation(cls, @selector(substringWithRange:), @selector(safeSubstringWithRange:)); 29 | 30 | }); 31 | } 32 | 33 | 34 | - (void)safeAppendString:(NSString *)aString 35 | { 36 | @try { 37 | 38 | [self safeAppendString:aString]; 39 | 40 | } @catch (NSException *exception) { 41 | 42 | hy_handleErrorWithException(exception); 43 | 44 | } @finally { 45 | 46 | } 47 | 48 | } 49 | 50 | - (void)safeInsertString:(NSString *)aString atIndex:(NSUInteger)loc 51 | { 52 | @try { 53 | 54 | [self safeInsertString:aString atIndex:loc]; 55 | 56 | } @catch (NSException *exception) { 57 | 58 | hy_handleErrorWithException(exception); 59 | 60 | } @finally { 61 | 62 | } 63 | 64 | } 65 | 66 | - (void)safeDeleteCharactersInRange:(NSRange)range 67 | { 68 | @try { 69 | 70 | [self safeDeleteCharactersInRange:range]; 71 | 72 | } @catch (NSException *exception) { 73 | 74 | // 如果是起点在字符串内,但是长度超过字符串,就删除掉起点之后的字符 75 | if (range.location < self.length) { 76 | 77 | [self safeDeleteCharactersInRange:NSMakeRange(range.location, self.length - 1 - range.location)]; 78 | 79 | } 80 | 81 | hy_handleErrorWithException(exception); 82 | 83 | } @finally { 84 | 85 | } 86 | 87 | } 88 | 89 | - (NSString *)safeSubstringFromIndex:(NSUInteger)from 90 | { 91 | NSString *string = self; 92 | 93 | @try { 94 | 95 | string = [self safeSubstringFromIndex:from]; 96 | 97 | } @catch (NSException *exception) { 98 | 99 | hy_handleErrorWithException(exception); 100 | 101 | } @finally { 102 | 103 | return string; 104 | } 105 | 106 | } 107 | 108 | - (NSString *)safeSubstringToIndex:(NSUInteger)to 109 | { 110 | NSString *string = self; 111 | 112 | @try { 113 | 114 | string = [self safeSubstringToIndex:to]; 115 | 116 | } @catch (NSException *exception) { 117 | 118 | hy_handleErrorWithException(exception); 119 | 120 | } @finally { 121 | 122 | return string; 123 | } 124 | 125 | } 126 | 127 | - (NSString *)safeSubstringWithRange:(NSRange)range 128 | { 129 | NSString *string = self; 130 | 131 | @try { 132 | 133 | string = [self safeSubstringWithRange:range]; 134 | 135 | } @catch (NSException *exception) { 136 | 137 | // 如果是起点在字符串内,但是长度超过字符串,就返回起点之后的字符 138 | if (range.location < self.length) { 139 | 140 | string = [self safeSubstringWithRange:NSMakeRange(range.location, self.length - 1 - range.location)]; 141 | 142 | } 143 | 144 | hy_handleErrorWithException(exception); 145 | 146 | } @finally { 147 | 148 | return string; 149 | } 150 | 151 | } 152 | 153 | @end 154 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSNotificationCenter+ExcepitionProtector.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSNotificationCenter+ExcepitionProtector.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/26. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HYExcepitionProtectedProtocol.h" 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NSNotificationCenter (ExcepitionProtector) 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSNotificationCenter+ExcepitionProtector.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNotificationCenter+ExcepitionProtector.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/26. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "NSNotificationCenter+ExcepitionProtector.h" 10 | #import "NSObject+HYHook.h" 11 | #import 12 | #import "HYExcepitionCollector.h" 13 | 14 | @implementation NSNotificationCenter (ExcepitionProtector) 15 | 16 | + (void)hy_swizzleMethod 17 | { 18 | static dispatch_once_t onceToken; 19 | dispatch_once(&onceToken, ^{ 20 | 21 | 22 | 23 | }); 24 | } 25 | 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSObject+ExcepitionProtectorSet.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+ExcepitionProtectorSet.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/26. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HYExcepitionProtectedProtocol.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NSObject (ExcepitionProtectorSet) 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSObject+ExcepitionProtectorSet.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+ExcepitionProtectorSet.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/26. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "NSObject+ExcepitionProtectorSet.h" 10 | #import "NSObject+HYHook.h" 11 | #import "NSObject+UnrecognizedSelectorProtector.h" 12 | #import "NSObject+KVOCrashProtector.h" 13 | 14 | @implementation NSObject (ExcepitionProtectorSet) 15 | 16 | + (void)hy_swizzleMethod 17 | { 18 | // 防止KVO崩溃的处理 19 | [self hy_KVOCrashProtectorSwizzleMethod]; 20 | 21 | // 防止未实现方法引起的崩溃 22 | [self hy_UnrecognizedSelectorProtectorSwizzleMethod]; 23 | 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSObject+HYHook.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+HYHook.h 3 | // HYUsersTraceRecordDemo 4 | // 5 | // Created by 张鸿运 on 2018/10/17. 6 | // Copyright © 2018年 张鸿运. All rights reserved. 7 | // 8 | 9 | 10 | //=================================== 11 | // 该分类为提供交换方法的工具类 12 | //=================================== 13 | #import 14 | 15 | 16 | /** 17 | 交换某个类的两个实例方法 18 | 19 | @param cls 要交换的类 20 | @param originSelector 原方法 21 | @param swizzledSelector 要交换的方法 22 | */ 23 | void hy_swizzleInstanceMethodImplementation( Class cls, SEL originSelector, SEL swizzledSelector ); 24 | 25 | /** 26 | 交换某个类的两个类方法 27 | 28 | @param cls 要交换的类 29 | @param originSelector 原方法 30 | @param swizzledSelector 要交换的方法 31 | */ 32 | void hy_swizzleClassMethodImplementation( Class cls, SEL originSelector, SEL swizzledSelector ); 33 | 34 | 35 | @interface NSObject (HYHook) 36 | 37 | /** 38 | 交换实例方法 39 | 40 | @param originSelector 原方法 41 | @param swizzledSelector 要交换的方法 42 | */ 43 | + (void)hy_swizzleInstanceMethodImplementation:(SEL)originSelector withSEL:(SEL)swizzledSelector; 44 | 45 | /** 46 | 交换类方法 47 | 48 | @param originSelector 原方法 49 | @param swizzledSelector 要交换的方法 50 | */ 51 | + (void)hy_swizzleClassMethodImplementation:(SEL)originSelector withSEL:(SEL)swizzledSelector; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSObject+HYHook.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+HYHook.m 3 | // HYUsersTraceRecordDemo 4 | // 5 | // Created by 张鸿运 on 2018/10/17. 6 | // Copyright © 2018年 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "NSObject+HYHook.h" 10 | #import 11 | 12 | void hy_swizzleInstanceMethodImplementation(Class cls, SEL originSelector, SEL swizzledSelector) 13 | { 14 | if (!cls) return; 15 | 16 | Method originalMethod = class_getInstanceMethod(cls, originSelector); 17 | Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector); 18 | 19 | BOOL didAddMethod = class_addMethod(cls, 20 | originSelector, 21 | method_getImplementation(swizzledMethod), 22 | method_getTypeEncoding(swizzledMethod)); 23 | 24 | if (didAddMethod) { 25 | class_replaceMethod(cls, 26 | swizzledSelector, 27 | method_getImplementation(originalMethod), 28 | method_getTypeEncoding(originalMethod)); 29 | } else { 30 | class_replaceMethod(cls, 31 | swizzledSelector, 32 | class_replaceMethod(cls, 33 | originSelector, 34 | method_getImplementation(swizzledMethod), 35 | method_getTypeEncoding(swizzledMethod)), 36 | method_getTypeEncoding(originalMethod)); 37 | // 用此方法引起未知崩溃,原因待查 38 | // method_exchangeImplementations(originalMethod, swizzledMethod); 39 | } 40 | } 41 | 42 | void hy_swizzleClassMethodImplementation(Class cls, SEL originSelector, SEL swizzledSelector) 43 | { 44 | if (!cls) return; 45 | 46 | Class metacls = objc_getMetaClass(NSStringFromClass(cls).UTF8String); 47 | 48 | Method originalMethod = class_getClassMethod(cls, originSelector); 49 | Method swizzledMethod = class_getClassMethod(cls, swizzledSelector); 50 | 51 | BOOL didAddMethod = class_addMethod(metacls, 52 | originSelector, 53 | method_getImplementation(swizzledMethod), 54 | method_getTypeEncoding(swizzledMethod)); 55 | if (didAddMethod) { 56 | class_replaceMethod(metacls, 57 | swizzledSelector, 58 | method_getImplementation(originalMethod), 59 | method_getTypeEncoding(originalMethod)); 60 | 61 | } else { 62 | method_exchangeImplementations(originalMethod, swizzledMethod); 63 | } 64 | } 65 | 66 | 67 | 68 | @implementation NSObject (HYHook) 69 | 70 | + (void)hy_swizzleInstanceMethodImplementation:(SEL)originSelector withSEL:(SEL)swizzledSelector 71 | { 72 | Class class = [self class]; 73 | 74 | hy_swizzleInstanceMethodImplementation(class, originSelector, swizzledSelector); 75 | } 76 | 77 | + (void)hy_swizzleClassMethodImplementation:(SEL)originSelector withSEL:(SEL)swizzledSelector 78 | { 79 | Class class = [self class]; 80 | 81 | hy_swizzleClassMethodImplementation(class, originSelector, swizzledSelector); 82 | 83 | } 84 | 85 | @end 86 | 87 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSObject+KVOCrashProtector.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+KVOCrashProtector.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/25. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NSObject (KVOCrashProtector) 14 | 15 | + (void)hy_KVOCrashProtectorSwizzleMethod; 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSObject+KVOCrashProtector.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+KVOCrashProtector.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/25. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "NSObject+KVOCrashProtector.h" 10 | #import 11 | #import 12 | #import "NSObject+HYHook.h" 13 | #import "HYExcepitionCollector.h" 14 | 15 | static const char DeallocKVOKey; 16 | static const char ObserverDeallocKVOKey; 17 | 18 | 19 | @interface KVOObjectItem : NSObject 20 | 21 | @property (nonatomic, strong) NSObject *observer; 22 | @property (nonatomic, copy) NSString *keyPath; 23 | @property (nonatomic, assign) NSKeyValueObservingOptions options; 24 | @property (nonatomic, assign) void *context; 25 | 26 | @end 27 | 28 | @implementation KVOObjectItem 29 | 30 | - (BOOL)isEqual:(KVOObjectItem *)object 31 | { 32 | if ([self.observer isEqual:object.observer] 33 | && [self.keyPath isEqualToString:object.keyPath]) return YES; 34 | 35 | return NO; 36 | } 37 | 38 | - (NSUInteger)hash 39 | { 40 | return [self.observer hash] ^ [self.keyPath hash]; 41 | } 42 | 43 | @end 44 | 45 | 46 | @interface KVOObjectContainer : NSObject 47 | 48 | @property (nonatomic, retain) NSMutableSet *kvoObjectSet; 49 | 50 | @property (nonatomic, unsafe_unretained) NSObject *whichObject; 51 | 52 | #if OS_OBJECT_HAVE_OBJC_SUPPORT 53 | @property (nonatomic, retain) dispatch_semaphore_t kvoLock; 54 | #else 55 | @property (nonatomic, assign) dispatch_semaphore_t kvoLock; 56 | #endif 57 | 58 | - (void)addKVOObjectItem:(KVOObjectItem *)item; 59 | 60 | - (void)removeKVOObjectItem:(KVOObjectItem *)item; 61 | 62 | - (BOOL)checkKVOItemExist:(KVOObjectItem *)item; 63 | 64 | @end 65 | 66 | @implementation KVOObjectContainer 67 | 68 | - (void)addKVOObjectItem:(KVOObjectItem *)item 69 | { 70 | if (item) { 71 | 72 | dispatch_semaphore_wait(self.kvoLock, DISPATCH_TIME_FOREVER); 73 | [self.kvoObjectSet addObject:item]; 74 | dispatch_semaphore_signal(self.kvoLock); 75 | } 76 | } 77 | 78 | - (void)removeKVOObjectItem:(KVOObjectItem *)item 79 | { 80 | if (item) { 81 | 82 | dispatch_semaphore_wait(self.kvoLock, DISPATCH_TIME_FOREVER); 83 | [self.kvoObjectSet removeObject:item]; 84 | dispatch_semaphore_signal(self.kvoLock); 85 | } 86 | } 87 | 88 | - (BOOL)checkKVOItemExist:(KVOObjectItem *)item 89 | { 90 | dispatch_semaphore_wait(self.kvoLock, DISPATCH_TIME_FOREVER); 91 | BOOL exist = NO; 92 | if (!item) { 93 | 94 | dispatch_semaphore_signal(self.kvoLock); 95 | return exist; 96 | 97 | } 98 | exist = [self.kvoObjectSet containsObject:item]; 99 | dispatch_semaphore_signal(self.kvoLock); 100 | 101 | return exist; 102 | } 103 | 104 | - (dispatch_semaphore_t)kvoLock 105 | { 106 | if (!_kvoLock) { 107 | 108 | _kvoLock = dispatch_semaphore_create(1); 109 | return _kvoLock; 110 | 111 | } 112 | 113 | return _kvoLock; 114 | } 115 | 116 | 117 | - (void)cleanKVOData 118 | { 119 | for (KVOObjectItem *item in self.kvoObjectSet) { 120 | #pragma clang diagnostic push 121 | #pragma clang diagnostic ignored "-Wundeclared-selector" 122 | @try { 123 | 124 | ((void(*)(id, SEL, id, NSString *))objc_msgSend)(self.whichObject, @selector(hookRemoveObserver:forKeyPath:), item.observer, item.keyPath); 125 | 126 | } 127 | @catch (NSException *exception) { 128 | 129 | } 130 | #pragma clang diagnostic pop 131 | } 132 | } 133 | 134 | - (NSMutableSet *)kvoObjectSet 135 | { 136 | if (!_kvoObjectSet) { 137 | 138 | _kvoObjectSet = [[NSMutableSet alloc] init]; 139 | } 140 | 141 | return _kvoObjectSet; 142 | } 143 | 144 | @end 145 | 146 | @interface HYObserverContainer : NSObject 147 | 148 | @property (nonatomic, retain) NSHashTable *observers; 149 | 150 | @property (nonatomic, assign) NSObject *whichObject; 151 | 152 | - (void)addObserver:(KVOObjectItem *)observer; 153 | 154 | - (void)removeObserver:(KVOObjectItem *)observer; 155 | 156 | @end 157 | 158 | @implementation HYObserverContainer 159 | 160 | - (instancetype)init 161 | { 162 | if (self = [super init]) { 163 | 164 | self.observers = [NSHashTable hashTableWithOptions:NSMapTableWeakMemory]; 165 | } 166 | 167 | return self; 168 | } 169 | 170 | - (void)addObserver:(KVOObjectItem *)observer 171 | { 172 | @synchronized (self) { 173 | 174 | [self.observers addObject:observer]; 175 | 176 | } 177 | } 178 | 179 | - (void)removeObserver:(KVOObjectItem *)observer 180 | { 181 | @synchronized (self) { 182 | 183 | [self.observers removeObject:observer]; 184 | 185 | } 186 | } 187 | 188 | - (void)cleanObservers 189 | { 190 | for (KVOObjectItem *item in self.observers) { 191 | 192 | [self.whichObject removeObserver:item.observer forKeyPath:item.keyPath]; 193 | 194 | } 195 | 196 | @synchronized (self) { 197 | 198 | [self.observers removeAllObjects]; 199 | } 200 | } 201 | 202 | @end 203 | 204 | @implementation NSObject (KVOCrashProtector) 205 | 206 | + (void)hy_KVOCrashProtectorSwizzleMethod 207 | { 208 | static dispatch_once_t onceToken; 209 | dispatch_once(&onceToken, ^{ 210 | 211 | hy_swizzleInstanceMethodImplementation([self class], @selector(addObserver:forKeyPath:options:context:), @selector(safeAddObserver:forKeyPath:options:context:)); 212 | hy_swizzleInstanceMethodImplementation([self class], @selector(removeObserver:forKeyPath:), @selector(safeRemoveObserver:forKeyPath:)); 213 | hy_swizzleInstanceMethodImplementation([self class], @selector(removeObserver:forKeyPath:context:), @selector(safeRemoveObserver:forKeyPath:context:)); 214 | 215 | }); 216 | 217 | } 218 | 219 | - (void)safeAddObserver:(NSObject *)observer 220 | forKeyPath:(NSString *)keyPath 221 | options:(NSKeyValueObservingOptions)options 222 | context:(void *)context 223 | { 224 | if ([self ignoreKVOInstanceClass:observer]) { 225 | 226 | [self safeAddObserver:observer 227 | forKeyPath:keyPath 228 | options:options 229 | context:context]; 230 | return; 231 | } 232 | 233 | if (!observer || keyPath.length == 0) return; 234 | 235 | KVOObjectContainer *objectContainer = objc_getAssociatedObject(self, &DeallocKVOKey); 236 | 237 | KVOObjectItem *item = [[KVOObjectItem alloc] init]; 238 | item.observer = observer; 239 | item.keyPath = keyPath; 240 | item.options = options; 241 | item.context = context; 242 | 243 | if (!objectContainer) { 244 | 245 | objectContainer = [[KVOObjectContainer alloc] init]; 246 | [objectContainer setWhichObject:self]; 247 | objc_setAssociatedObject(self, &DeallocKVOKey, objectContainer, OBJC_ASSOCIATION_RETAIN); 248 | } 249 | 250 | if (![objectContainer checkKVOItemExist:item]) { 251 | 252 | [objectContainer addKVOObjectItem:item]; 253 | [self safeAddObserver:observer 254 | forKeyPath:keyPath 255 | options:options 256 | context:context]; 257 | } 258 | 259 | HYObserverContainer *observerContainer = objc_getAssociatedObject(observer, &ObserverDeallocKVOKey); 260 | 261 | if (!observerContainer) { 262 | 263 | observerContainer = [[HYObserverContainer alloc] init]; 264 | [observerContainer setWhichObject:self]; 265 | [observerContainer addObserver:item]; 266 | objc_setAssociatedObject(observer, &ObserverDeallocKVOKey, observerContainer, OBJC_ASSOCIATION_RETAIN); 267 | 268 | } else { 269 | 270 | [observerContainer addObserver:item]; 271 | } 272 | 273 | 274 | } 275 | 276 | - (void)safeRemoveObserver:(NSObject *)observer 277 | forKeyPath:(NSString *)keyPath 278 | context:(void *)context 279 | { 280 | if ([self ignoreKVOInstanceClass:observer]) { 281 | 282 | [self safeRemoveObserver:observer 283 | forKeyPath:keyPath 284 | context:context]; 285 | return; 286 | } 287 | 288 | [self removeObserver:observer forKeyPath:keyPath]; 289 | } 290 | 291 | - (void)safeRemoveObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath 292 | { 293 | if ([self ignoreKVOInstanceClass:observer]) { 294 | 295 | [self safeRemoveObserver:observer forKeyPath:keyPath]; 296 | 297 | return; 298 | } 299 | 300 | KVOObjectContainer *objectContainer = objc_getAssociatedObject(self, &DeallocKVOKey); 301 | 302 | if (!observer || !objectContainer) return; 303 | 304 | KVOObjectItem *item = [[KVOObjectItem alloc] init]; 305 | item.observer = observer; 306 | item.keyPath = keyPath; 307 | 308 | if ([objectContainer checkKVOItemExist:item]) { 309 | 310 | @try { 311 | 312 | [self safeRemoveObserver:observer forKeyPath:keyPath]; 313 | } 314 | @catch (NSException *exception) { 315 | 316 | hy_handleErrorWithException(exception); 317 | } 318 | 319 | [objectContainer removeKVOObjectItem:item]; 320 | 321 | } else { 322 | 323 | NSException *exception = [[NSException alloc] initWithName:@"" 324 | reason:@"" 325 | userInfo:nil]; 326 | hy_handleErrorWithException(exception); 327 | } 328 | 329 | } 330 | 331 | - (BOOL)ignoreKVOInstanceClass:(id)object 332 | { 333 | 334 | if (!object) return NO; 335 | 336 | // Ignore ReactiveCocoa 337 | if (object_getClass(object) == objc_getClass("RACKVOProxy")) return YES; 338 | 339 | // Ignore AMAP 340 | NSString *className = NSStringFromClass(object_getClass(object)); 341 | if ([className hasPrefix:@"AMap"]) return YES; 342 | 343 | return NO; 344 | } 345 | 346 | 347 | - (void)hy_cleanKVO 348 | { 349 | 350 | KVOObjectContainer *objectContainer = objc_getAssociatedObject(self, &DeallocKVOKey); 351 | HYObserverContainer *observerContainer = objc_getAssociatedObject(self, &ObserverDeallocKVOKey); 352 | 353 | if (objectContainer) { 354 | 355 | [objectContainer cleanKVOData]; 356 | 357 | } else if (observerContainer) { 358 | 359 | [observerContainer cleanObservers]; 360 | 361 | } 362 | } 363 | 364 | @end 365 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSObject+UnrecognizedSelectorProtector.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+UnrecognizedSelectorProtector.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/19. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | //==================================================== 10 | // 该分类为防止unrecognized selector引起的崩溃的分类 11 | //==================================================== 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface NSObject (UnrecognizedSelectorProtector) 17 | 18 | + (void)hy_UnrecognizedSelectorProtectorSwizzleMethod; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSObject+UnrecognizedSelectorProtector.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+UnrecognizedSelectorProtector.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/19. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "NSObject+UnrecognizedSelectorProtector.h" 10 | #import "NSObject+HYHook.h" 11 | #import 12 | #import "HYExcepitionCollector.h" 13 | 14 | @implementation NSObject (UnrecognizedSelectorProtector) 15 | 16 | + (void)hy_UnrecognizedSelectorProtectorSwizzleMethod 17 | { 18 | hy_swizzleInstanceMethodImplementation([self class], @selector(methodSignatureForSelector:), @selector(safeMethodSignatureForSelector:)); 19 | hy_swizzleInstanceMethodImplementation([self class], @selector(forwardInvocation:), @selector(safeForwardInvocation:)); 20 | } 21 | 22 | 23 | - (NSMethodSignature *)safeMethodSignatureForSelector:(SEL)aSelector 24 | { 25 | NSMethodSignature *methodSignature = [self safeMethodSignatureForSelector:aSelector]; 26 | if (methodSignature) return methodSignature; 27 | 28 | 29 | IMP originIMP = class_getMethodImplementation([NSObject class], @selector(methodSignatureForSelector:)); 30 | IMP currentClassIMP = class_getMethodImplementation([self class], @selector(methodSignatureForSelector:)); 31 | // 如果子类重载了该方法,则返回nil 32 | if (originIMP != currentClassIMP) return nil; 33 | 34 | 35 | // - (void)xxxx 36 | return [NSMethodSignature signatureWithObjCTypes:"v@:"]; 37 | } 38 | 39 | - (void)safeForwardInvocation:(NSInvocation *)invocation 40 | { 41 | NSString *reason = [NSString stringWithFormat:@"class:[%@] not found selector:(%@)",NSStringFromClass(self.class),NSStringFromSelector(invocation.selector)]; 42 | 43 | NSException *exception = [NSException exceptionWithName:@"Unrecognized Selector" 44 | reason:reason 45 | userInfo:nil]; 46 | // 收集错误信息 47 | hy_handleErrorWithException(exception); 48 | 49 | } 50 | 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSSet+ExcepitionProtector.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSSet+ExcepitionProtector.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/26. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HYExcepitionProtectedProtocol.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NSSet (ExcepitionProtector) 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSSet+ExcepitionProtector.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSSet+ExcepitionProtector.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/26. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "NSSet+ExcepitionProtector.h" 10 | #import "NSObject+HYHook.h" 11 | #import 12 | #import "HYExcepitionCollector.h" 13 | 14 | @implementation NSSet (ExcepitionProtector) 15 | 16 | + (void)hy_swizzleMethod 17 | { 18 | static dispatch_once_t onceToken; 19 | dispatch_once(&onceToken, ^{ 20 | 21 | Class cls = objc_getClass("NSSet"); 22 | hy_swizzleClassMethodImplementation(cls, @selector(setWithObject:), @selector(safeSetWithObject:)); 23 | 24 | }); 25 | } 26 | 27 | + (instancetype)safeSetWithObject:(id)object 28 | { 29 | 30 | id instance = nil; 31 | 32 | @try { 33 | 34 | instance = [self safeSetWithObject:object]; 35 | 36 | } @catch (NSException *exception) { 37 | 38 | hy_handleErrorWithException(exception); 39 | 40 | } @finally { 41 | 42 | return instance; 43 | 44 | } 45 | 46 | } 47 | 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSString+ExcepitionProtector.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+ExcepitionProtector.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/26. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HYExcepitionProtectedProtocol.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NSString (ExcepitionProtector) 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSString+ExcepitionProtector.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+ExcepitionProtector.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/26. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "NSString+ExcepitionProtector.h" 10 | #import "NSObject+HYHook.h" 11 | #import 12 | #import "HYExcepitionCollector.h" 13 | 14 | @implementation NSString (ExcepitionProtector) 15 | 16 | + (void)hy_swizzleMethod 17 | { 18 | static dispatch_once_t onceToken; 19 | dispatch_once(&onceToken, ^{ 20 | 21 | Class cls = objc_getClass("NSString"); 22 | hy_swizzleClassMethodImplementation(cls, @selector(stringWithUTF8String:), @selector(safeStringWithUTF8String:)); 23 | hy_swizzleClassMethodImplementation(cls, @selector(stringWithCString:encoding:), @selector(safeStringWithCString:encoding:)); 24 | 25 | Class clsPlaceholderString = objc_getClass("NSPlaceholderString"); 26 | hy_swizzleInstanceMethodImplementation(clsPlaceholderString, @selector(initWithCString:encoding:), @selector(safeInitWithCString:encoding:)); 27 | hy_swizzleInstanceMethodImplementation(clsPlaceholderString, @selector(initWithString:), @selector(safeInitWithString:)); 28 | 29 | 30 | Class clsNSCFConstantString = objc_getClass("__NSCFConstantString"); 31 | hy_swizzleInstanceMethodImplementation(clsNSCFConstantString, @selector(substringFromIndex:), @selector(safeSubstringFromIndex:)); 32 | hy_swizzleInstanceMethodImplementation(clsNSCFConstantString, @selector(substringToIndex:), @selector(safeSubstringToIndex:)); 33 | hy_swizzleInstanceMethodImplementation(clsNSCFConstantString, @selector(substringWithRange:), @selector(safeSubstringWithRange:)); 34 | hy_swizzleInstanceMethodImplementation(clsNSCFConstantString, @selector(rangeOfString:options:range:locale:), @selector(safeRangeOfString:options:range:locale:)); 35 | 36 | 37 | Class clsTaggedPointerString = objc_getClass("NSTaggedPointerString"); 38 | hy_swizzleInstanceMethodImplementation(clsTaggedPointerString, @selector(substringFromIndex:), @selector(safeSubstringFromIndex:)); 39 | hy_swizzleInstanceMethodImplementation(clsTaggedPointerString, @selector(substringToIndex:), @selector(safeSubstringToIndex:)); 40 | hy_swizzleInstanceMethodImplementation(clsTaggedPointerString, @selector(substringWithRange:), @selector(safeSubstringWithRange:)); 41 | hy_swizzleInstanceMethodImplementation(clsTaggedPointerString, @selector(rangeOfString:options:range:locale:), @selector(safeRangeOfString:options:range:locale:)); 42 | 43 | }); 44 | } 45 | 46 | 47 | 48 | + (NSString *)safeStringWithUTF8String:(const char *)nullTerminatedCString 49 | { 50 | NSString *string = nil; 51 | 52 | @try { 53 | 54 | string = [self safeStringWithUTF8String:nullTerminatedCString]; 55 | 56 | } @catch (NSException *exception) { 57 | 58 | hy_handleErrorWithException(exception); 59 | 60 | } @finally { 61 | 62 | return string; 63 | 64 | } 65 | 66 | } 67 | 68 | + (nullable instancetype)safeStringWithCString:(const char *)cString encoding:(NSStringEncoding)enc 69 | { 70 | NSString *string = nil; 71 | 72 | @try { 73 | 74 | string = [self safeStringWithCString:cString encoding:enc]; 75 | 76 | } @catch (NSException *exception) { 77 | 78 | hy_handleErrorWithException(exception); 79 | 80 | } @finally { 81 | 82 | return string; 83 | 84 | } 85 | 86 | } 87 | 88 | - (nullable instancetype)safeInitWithString:(id)cString 89 | { 90 | NSString *string = nil; 91 | 92 | @try { 93 | 94 | string = [self safeInitWithString:cString]; 95 | 96 | } @catch (NSException *exception) { 97 | 98 | hy_handleErrorWithException(exception); 99 | 100 | } @finally { 101 | 102 | return string; 103 | 104 | } 105 | 106 | } 107 | 108 | - (nullable instancetype)safeInitWithCString:(const char *)nullTerminatedCString encoding:(NSStringEncoding)encoding 109 | { 110 | NSString *string = nil; 111 | 112 | @try { 113 | 114 | string = [self safeInitWithCString:nullTerminatedCString encoding:encoding]; 115 | 116 | } @catch (NSException *exception) { 117 | 118 | hy_handleErrorWithException(exception); 119 | 120 | } @finally { 121 | 122 | return string; 123 | 124 | } 125 | 126 | } 127 | 128 | - (NSString *)safeSubstringFromIndex:(NSUInteger)from 129 | { 130 | 131 | NSString *string = self; 132 | 133 | @try { 134 | 135 | string = [self safeSubstringFromIndex:from]; 136 | 137 | } @catch (NSException *exception) { 138 | 139 | hy_handleErrorWithException(exception); 140 | 141 | } @finally { 142 | 143 | return string; 144 | 145 | } 146 | 147 | } 148 | 149 | - (NSString *)safeSubstringToIndex:(NSUInteger)to 150 | { 151 | NSString *string = self; 152 | 153 | @try { 154 | 155 | string = [self safeSubstringToIndex:to]; 156 | 157 | } @catch (NSException *exception) { 158 | 159 | hy_handleErrorWithException(exception); 160 | 161 | } @finally { 162 | 163 | return string; 164 | 165 | } 166 | 167 | } 168 | 169 | - (NSString *)safeSubstringWithRange:(NSRange)range 170 | { 171 | 172 | NSString *string = self; 173 | 174 | @try { 175 | 176 | string = [self safeSubstringWithRange:range]; 177 | 178 | } @catch (NSException *exception) { 179 | 180 | // 如果是起点在字符串内,但是长度超过字符串,就返回起点之后的字符 181 | if (range.location < self.length) { 182 | 183 | string = [self safeSubstringWithRange:NSMakeRange(range.location, self.length - 1 - range.location)]; 184 | 185 | } 186 | 187 | hy_handleErrorWithException(exception); 188 | 189 | } @finally { 190 | 191 | return string; 192 | } 193 | 194 | } 195 | - (NSRange)safeRangeOfString:(NSString *)searchString 196 | options:(NSStringCompareOptions)mask 197 | range:(NSRange)range 198 | locale:(nullable NSLocale *)locale 199 | { 200 | 201 | NSRange returnRange; 202 | 203 | @try { 204 | 205 | returnRange = [self safeRangeOfString:searchString options:mask range:range locale:locale]; 206 | 207 | } @catch (NSException *exception) { 208 | 209 | if (searchString && range.location < self.length) { 210 | 211 | NSRange safeRange = NSMakeRange(range.location, self.length - 1 - range.location); 212 | returnRange = [self safeRangeOfString:searchString 213 | options:mask 214 | range:safeRange 215 | locale:locale]; 216 | 217 | } 218 | 219 | hy_handleErrorWithException(exception); 220 | 221 | } @finally { 222 | 223 | return returnRange; 224 | 225 | } 226 | 227 | } 228 | 229 | @end 230 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSTimer+ExcepitionProtector.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSTimer+ExcepitionProtector.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/26. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HYExcepitionProtectedProtocol.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NSTimer (ExcepitionProtector) 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/NSTimer+ExcepitionProtector.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSTimer+ExcepitionProtector.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/26. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "NSTimer+ExcepitionProtector.h" 10 | #import "NSObject+HYHook.h" 11 | #import 12 | #import "HYExcepitionCollector.h" 13 | 14 | @interface TimerTagetAgent : NSObject 15 | 16 | @property (nonatomic, assign) NSTimeInterval ti; 17 | @property (nonatomic, weak) id target; 18 | @property (nonatomic, assign) SEL selector; 19 | @property (nonatomic, assign) id userInfo; 20 | @property (nonatomic, weak) NSTimer *timer; 21 | @property (nonatomic, copy) NSString *targetClassName; 22 | @property (nonatomic, copy) NSString *targetMethodName; 23 | 24 | @end 25 | 26 | 27 | @implementation TimerTagetAgent 28 | 29 | - (void)fireTimer 30 | { 31 | if (!self.target) { 32 | 33 | [self.timer invalidate]; 34 | self.timer = nil; 35 | NSString *reason = [NSString stringWithFormat:@"In [%@ %@], a instance has release, but the timer has not invalidate", self.targetClassName, self.targetMethodName]; 36 | NSException *exception = [[NSException alloc] initWithName:@"NSTimer Exception" 37 | reason:reason 38 | userInfo:nil]; 39 | hy_handleErrorWithException(exception); 40 | return; 41 | } 42 | 43 | if ([self.target respondsToSelector:self.selector]) { 44 | #pragma clang diagnostic push 45 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 46 | [self.target performSelector:self.selector withObject:self.timer]; 47 | #pragma clang diagnostic pop 48 | } 49 | } 50 | 51 | @end 52 | 53 | @implementation NSTimer (ExcepitionProtector) 54 | 55 | + (void)hy_swizzleMethod 56 | { 57 | static dispatch_once_t onceToken; 58 | dispatch_once(&onceToken, ^{ 59 | 60 | hy_swizzleClassMethodImplementation([NSTimer class], @selector(scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:), @selector(safeScheduledTimerWithTimeInterval:target:selector:userInfo:repeats:)); 61 | 62 | 63 | }); 64 | } 65 | 66 | + (NSTimer *)safeScheduledTimerWithTimeInterval:(NSTimeInterval)ti 67 | target:(id)aTarget 68 | selector:(SEL)aSelector 69 | userInfo:(nullable id)userInfo 70 | repeats:(BOOL)yesOrNo 71 | { 72 | if (!yesOrNo) { 73 | 74 | return [self safeScheduledTimerWithTimeInterval:ti 75 | target:aTarget 76 | selector:aSelector 77 | userInfo:userInfo 78 | repeats:yesOrNo]; 79 | } 80 | 81 | TimerTagetAgent *agent = [TimerTagetAgent new]; 82 | agent.ti = ti; 83 | agent.target = aTarget; 84 | agent.selector = aSelector; 85 | agent.userInfo = userInfo; 86 | 87 | if (aTarget) { 88 | 89 | agent.targetClassName = [NSString stringWithCString:object_getClassName(aTarget) 90 | encoding:NSASCIIStringEncoding]; 91 | } 92 | agent.targetMethodName = NSStringFromSelector(aSelector); 93 | 94 | NSTimer *timer = [NSTimer safeScheduledTimerWithTimeInterval:ti 95 | target:agent 96 | selector:@selector(fireTimer) 97 | userInfo:userInfo 98 | repeats:yesOrNo]; 99 | agent.timer = timer; 100 | 101 | return timer; 102 | 103 | } 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/UIView+ExcepitionProtector.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+ExcepitionProtector.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/25. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | //====================================================== 10 | // 该分类为开发过程中,在子线程刷新UI提醒类 11 | // DEBUG模式下,会使程序崩溃,提示开发过程中在子线程刷新UI问题 12 | //====================================================== 13 | 14 | 15 | #import 16 | #import "HYExcepitionProtectedProtocol.h" 17 | 18 | NS_ASSUME_NONNULL_BEGIN 19 | 20 | @interface UIView (ExcepitionProtector) 21 | 22 | @end 23 | 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYExceptionProtector/UIView+ExcepitionProtector.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+ExcepitionProtector.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/25. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "UIView+ExcepitionProtector.h" 10 | #import "HYExcepitionCollector.h" 11 | #import 12 | #import "NSObject+HYHook.h" 13 | 14 | @implementation UIView (ExcepitionProtector) 15 | 16 | + (void)hy_swizzleMethod 17 | { 18 | static dispatch_once_t onceToken; 19 | dispatch_once(&onceToken, ^{ 20 | 21 | Class cls = objc_getClass("UIView"); 22 | hy_swizzleInstanceMethodImplementation(cls, @selector(setNeedsLayout), @selector(safeSetNeedsLayout)); 23 | hy_swizzleInstanceMethodImplementation(cls, @selector(setNeedsDisplay), @selector(safeSetNeedsDisplay)); 24 | hy_swizzleInstanceMethodImplementation(cls, @selector(setNeedsDisplayInRect:), @selector(safeSetNeedsDisplayInRect:)); 25 | 26 | }); 27 | } 28 | 29 | 30 | - (void)safeSetNeedsLayout 31 | { 32 | 33 | if (![NSThread isMainThread]) { 34 | 35 | dispatch_async(dispatch_get_main_queue(), ^{ 36 | 37 | [self safeSetNeedsLayout]; 38 | 39 | }); 40 | 41 | #if DEBUG 42 | NSString *message = [NSString stringWithFormat:@"You can not display UI on a background thread,-[%@ safeSetNeedsLayout]", [self class]]; 43 | NSAssert(false, message); 44 | #endif 45 | 46 | } 47 | 48 | [self safeSetNeedsLayout]; 49 | 50 | } 51 | 52 | - (void)safeSetNeedsDisplay 53 | { 54 | 55 | if (![NSThread isMainThread]) { 56 | 57 | dispatch_async(dispatch_get_main_queue(), ^{ 58 | 59 | [self safeSetNeedsDisplay]; 60 | 61 | }); 62 | 63 | #if DEBUG 64 | NSString *message = [NSString stringWithFormat:@"You can not display UI on a background thread,-[%@ safeSetNeedsDisplay]", [self class]]; 65 | NSAssert(false, message); 66 | #endif 67 | 68 | } 69 | 70 | [self safeSetNeedsDisplay]; 71 | 72 | } 73 | 74 | - (void)safeSetNeedsDisplayInRect:(CGRect)rect 75 | { 76 | if (![NSThread isMainThread]) { 77 | 78 | dispatch_async(dispatch_get_main_queue(), ^{ 79 | 80 | [self safeSetNeedsDisplayInRect:rect]; 81 | 82 | }); 83 | 84 | #if DEBUG 85 | 86 | NSString *message = [NSString stringWithFormat:@"You can not display UI on a background thread,-[%@ safeSetNeedsDisplayInRect:]", [self class]]; 87 | NSAssert(false, message); 88 | #endif 89 | 90 | } 91 | 92 | [self safeSetNeedsDisplayInRect:rect]; 93 | 94 | } 95 | 96 | 97 | @end 98 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYView.h: -------------------------------------------------------------------------------- 1 | // 2 | // HYView.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/25. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface HYView : UIView 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /HYExceptionProtector/HYView.m: -------------------------------------------------------------------------------- 1 | // 2 | // HYView.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/25. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "HYView.h" 10 | 11 | @implementation HYView 12 | 13 | /* 14 | // Only override drawRect: if you perform custom drawing. 15 | // An empty implementation adversely affects performance during animation. 16 | - (void)drawRect:(CGRect)rect { 17 | // Drawing code 18 | } 19 | */ 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /HYExceptionProtector/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /HYExceptionProtector/Person.h: -------------------------------------------------------------------------------- 1 | // 2 | // Person.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/19. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @protocol PersonDelegate 14 | 15 | - (void)eat; 16 | 17 | @end 18 | 19 | @interface Person : NSObject 20 | 21 | @property (nonatomic, copy) NSString *name; 22 | 23 | - (void)eat; 24 | 25 | - (void)timeTest; 26 | 27 | @end 28 | 29 | NS_ASSUME_NONNULL_END 30 | -------------------------------------------------------------------------------- /HYExceptionProtector/Person.m: -------------------------------------------------------------------------------- 1 | // 2 | // Person.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/12/19. 6 | // Copyright © 2018 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "Person.h" 10 | 11 | @implementation Person 12 | 13 | - (void)eat 14 | { 15 | NSLog(@"eat"); 16 | } 17 | 18 | - (void)timeTest 19 | { 20 | NSLog(@"hello"); 21 | NSLog(@"%@", self); 22 | } 23 | 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /HYExceptionProtector/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/10/22. 6 | // Copyright © 2018年 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /HYExceptionProtector/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/10/22. 6 | // Copyright © 2018年 张鸿运. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "HYExcepitionProtector.h" 11 | #import 12 | 13 | #import "Person.h" 14 | #import "HYView.h" 15 | 16 | @interface ViewController () 17 | @property (nonatomic, strong) Person *per; 18 | @end 19 | 20 | @implementation ViewController 21 | 22 | - (void)viewDidLoad 23 | { 24 | [super viewDidLoad]; 25 | 26 | [[HYExcepitionProtector shareInstance] configAllProtectTypes]; 27 | [[HYExcepitionProtector shareInstance] startProtection]; 28 | 29 | NSArray *array = [self findAllOf:[NSDictionary class]]; 30 | NSLog(@"array = %@",array); 31 | 32 | _per = [[Person alloc] init]; 33 | // NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 34 | // target:_per 35 | // selector:@selector(timeTest) 36 | // userInfo:nil 37 | // repeats:YES]; 38 | // [timer fire]; 39 | } 40 | 41 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 42 | { 43 | 44 | // 测试数组崩溃事件 45 | // [self testArray]; 46 | 47 | // 测试字典崩溃事件 48 | // [self testDict]; 49 | 50 | // 测试在子线程刷新试图 51 | // [self testView]; 52 | 53 | // 测试未实现方法 54 | // [self testUnrecognizedSelector]; 55 | 56 | // 测试KVO 57 | // [self testKVO]; 58 | // _per.name = @"hello"; 59 | // [_per removeObserver:self forKeyPath:@"name"]; 60 | 61 | _per = nil; 62 | NSLog(@"%@", _per); 63 | 64 | 65 | NSMutableSet *mSet = [NSMutableSet setWithObject:@"1"]; 66 | // [mSet addObject:nil]; 67 | // [mSet removeObject:nil]; 68 | 69 | 70 | } 71 | 72 | - (void)observeValueForKeyPath:(NSString *)keyPath 73 | ofObject:(id)object 74 | change:(NSDictionary *)change 75 | context:(void *)context 76 | { 77 | NSLog(@"%@",keyPath); 78 | } 79 | 80 | - (void)testArray 81 | { 82 | NSArray *arr00 = [[NSArray alloc] init]; 83 | NSArray *arr01 = [[NSArray alloc] initWithObjects:@"1", nil]; 84 | NSArray *arr02 = [[NSArray alloc] initWithObjects:@"1", nil]; 85 | NSArray *arr03 = [[NSArray alloc] initWithObjects:@"1", @"2", nil]; 86 | NSArray *arr04 = [[NSArray alloc] initWithObjects:@"1", @"2", @"3", nil]; 87 | 88 | NSArray *arr05 = @[]; 89 | NSArray *arr06 = @[@"1"]; 90 | NSArray *arr07 = @[@"1",@"2",]; 91 | NSArray *arr08 = @[@"1",@"2",@"3"]; 92 | 93 | NSArray *arr09 = [NSArray arrayWithArray:arr01]; 94 | 95 | NSLog(@"arr00 = %@",[arr00 class]); 96 | NSLog(@"arr01 = %@",[arr01 class]); 97 | NSLog(@"arr02 = %@",[arr02 class]); 98 | NSLog(@"arr03 = %@",[arr03 class]); 99 | NSLog(@"arr04 = %@",[arr04 class]); 100 | NSLog(@"arr05 = %@",[arr05 class]); 101 | NSLog(@"arr06 = %@",[arr06 class]); 102 | NSLog(@"arr07 = %@",[arr07 class]); 103 | NSLog(@"arr08 = %@",[arr08 class]); 104 | NSLog(@"arr09 = %@",[arr09 class]); 105 | 106 | 107 | [arr01 objectAtIndex:4]; 108 | [arr02 subarrayWithRange:NSMakeRange(1, 2)]; 109 | [arr03 objectAtIndex:4]; 110 | [arr04 objectAtIndex:4]; 111 | } 112 | 113 | 114 | - (void)testDict 115 | { 116 | NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithCapacity:10]; 117 | [dictionary setObject:@"1" forKey:@"11"]; 118 | [dictionary removeObjectForKey:nil]; 119 | NSDictionary *dic = [[NSDictionary alloc] initWithObjects:@[@"1"] forKeys:@[]]; 120 | } 121 | 122 | - (void)testView 123 | { 124 | HYView *view = [[HYView alloc] init]; 125 | 126 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 127 | 128 | [view setNeedsDisplay]; 129 | 130 | }); 131 | } 132 | 133 | - (void)testUnrecognizedSelector 134 | { 135 | Person *p = [Person new]; 136 | [p eat]; 137 | } 138 | 139 | - (void)testKVO 140 | { 141 | _per = [[Person alloc] init]; 142 | [_per addObserver:self 143 | forKeyPath:@"name" 144 | options:NSKeyValueObservingOptionNew 145 | context:nil]; 146 | 147 | [_per addObserver:self 148 | forKeyPath:@"name" 149 | options:NSKeyValueObservingOptionNew 150 | context:nil]; 151 | 152 | [_per addObserver:self 153 | forKeyPath:@"name" 154 | options:NSKeyValueObservingOptionNew 155 | context:nil]; 156 | 157 | [_per addObserver:self 158 | forKeyPath:@"name" 159 | options:NSKeyValueObservingOptionNew 160 | context:nil]; 161 | 162 | [_per addObserver:self 163 | forKeyPath:@"name" 164 | options:NSKeyValueObservingOptionNew 165 | context:nil]; 166 | } 167 | 168 | - (NSArray *)findAllOf:(Class)defaultClass 169 | { 170 | int count = objc_getClassList(NULL, 0); 171 | 172 | if (count <= 0) { 173 | 174 | @throw @"Couldn't retrieve Obj-C class-list"; 175 | 176 | return @[defaultClass]; 177 | } 178 | 179 | NSMutableArray *output = @[].mutableCopy; 180 | 181 | Class *classes = (Class *) malloc(sizeof(Class) * count); 182 | 183 | objc_getClassList(classes, count); 184 | 185 | for (int i = 0; i < count; ++i) { 186 | 187 | if (defaultClass == class_getSuperclass(classes[i])) { 188 | 189 | [output addObject:classes[i]]; 190 | } 191 | 192 | } 193 | 194 | free(classes); 195 | 196 | return output.copy; 197 | 198 | } 199 | 200 | @end 201 | -------------------------------------------------------------------------------- /HYExceptionProtector/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // HYExceptionProtector 4 | // 5 | // Created by 张鸿运 on 2018/10/22. 6 | // Copyright © 2018年 张鸿运. 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 | -------------------------------------------------------------------------------- /HYExceptionProtectorTests/HYExceptionProtectorTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // HYExceptionProtectorTests.m 3 | // HYExceptionProtectorTests 4 | // 5 | // Created by 张鸿运 on 2018/10/22. 6 | // Copyright © 2018年 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HYExceptionProtectorTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation HYExceptionProtectorTests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | } 20 | 21 | - (void)tearDown { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | - (void)testExample { 26 | // This is an example of a functional test case. 27 | // Use XCTAssert and related functions to verify your tests produce the correct results. 28 | } 29 | 30 | - (void)testPerformanceExample { 31 | // This is an example of a performance test case. 32 | [self measureBlock:^{ 33 | // Put the code you want to measure the time of here. 34 | }]; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /HYExceptionProtectorTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /HYExceptionProtectorUITests/HYExceptionProtectorUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // HYExceptionProtectorUITests.m 3 | // HYExceptionProtectorUITests 4 | // 5 | // Created by 张鸿运 on 2018/10/22. 6 | // Copyright © 2018年 张鸿运. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HYExceptionProtectorUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation HYExceptionProtectorUITests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | 20 | // In UI tests it is usually best to stop immediately when a failure occurs. 21 | self.continueAfterFailure = NO; 22 | 23 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 24 | [[[XCUIApplication alloc] init] launch]; 25 | 26 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 27 | } 28 | 29 | - (void)tearDown { 30 | // Put teardown code here. This method is called after the invocation of each test method in the class. 31 | } 32 | 33 | - (void)testExample { 34 | // Use recording to get started writing UI tests. 35 | // Use XCTAssert and related functions to verify your tests produce the correct results. 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /HYExceptionProtectorUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## iOS应用Crash保护系统 2 | ### 方案: 3 | 参照简书:[iOS应用Crash保护系统](https://www.jianshu.com/p/eb713b1f22dc) 4 | ------- 5 | ### 功能: 6 | 对iOS APP进行崩溃保护,并对崩溃信息进行收集,可以根据需要上传到服务器,可以保护线上应用不闪退,并且收集到错误促使开发人员改正;该系统z从以下几个方面对APP进行保护 7 | * ```unrecognized selector```引起的崩溃 8 | * 容器类数据类型操作引起的崩溃 9 | * 字符串操作引起的崩溃 10 | * ```KVO```引起的崩溃 11 | * ```NSTimer```引起的崩溃 12 | * 非主线程刷新UI 13 | * 野指针 14 | * ```NSNotification```引起的崩溃 15 | 16 | ------ 17 | ### 使用: 18 | 1. 导入头文件 19 | ``` 20 | #import "HYExcepitionProtector.h" 21 | ``` 22 | 2.开启保护 23 | ``` 24 | [[HYExcepitionProtector shareInstance] configAllProtectTypes]; 25 | [[HYExcepitionProtector shareInstance] startProtection]; 26 | ``` 27 | 28 | --------------------------------------------------------------------------------