├── .gitignore ├── FFExtension.podspec ├── FFExtension.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── hufeng.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── FFExtension ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Classes │ ├── Core │ │ ├── FFCommonMacro.h │ │ ├── FFExceptionProxy.h │ │ ├── FFExceptionProxy.m │ │ ├── NSObject+methodSwizzle.h │ │ └── NSObject+methodSwizzle.m │ ├── Foundation │ │ ├── FFManager.h │ │ ├── FFManager.m │ │ ├── NSAttributedString+FFExtension.h │ │ ├── NSAttributedString+FFExtension.m │ │ ├── NSCache+FFExtension.h │ │ ├── NSCache+FFExtension.m │ │ ├── NSData+FFExtension.h │ │ ├── NSData+FFExtension.m │ │ ├── NSDictionary+FFExtension.h │ │ ├── NSDictionary+FFExtension.m │ │ ├── NSObject+FFExtension.h │ │ ├── NSObject+FFExtension.m │ │ ├── NSSet+FFExtension.h │ │ ├── NSSet+FFExtension.m │ │ ├── NSString+FFExtension.h │ │ ├── NSString+FFExtension.m │ │ ├── NSUserDefaults+FFExtension.h │ │ └── NSUserDefaults+FFExtension.m │ └── MRC │ │ ├── NSArray+FFExtension.h │ │ └── NSArray+FFExtension.m ├── Info.plist ├── SSZTestObject.h ├── SSZTestObject.m ├── ViewController.h ├── ViewController.m └── main.m ├── FFExtensionTests ├── FFExtensionTests.m └── Info.plist ├── FFExtensionUITests ├── FFExtensionUITests.m └── Info.plist ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.xcuserstate 3 | *.xccheckout 4 | *.xcuserdatad 5 | *.xcuserdata 6 | xcuserdata/ 7 | xcuserdata 8 | project.xcworkspace/ 9 | *.xcbkptlist 10 | *.xcscmblueprint 11 | Breakpoints_v2.xcbkptlist 12 | Pods/ 13 | node_modules/ 14 | node_modules 15 | -------------------------------------------------------------------------------- /FFExtension.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = 'FFExtension' 4 | s.version = '1.2.2' 5 | s.platform = :ios, '8.0' 6 | s.ios.deployment_target = '8.0' 7 | s.license = 'MIT' 8 | s.homepage = 'https://github.com/kobe1941/FFExtension' 9 | s.author = { 'hufeng' => 'fenng.hu@qq.com' } 10 | s.source = { :git => 'https://github.com/kobe1941/FFExtension.git', :tag => s.version } 11 | s.summary = 'To reduce the crash in ios app' 12 | s.description = 'if you have any problem or crash with use FFExtension, please issue me and paste detail crash logs, Have a nice day(#^.^#).' 13 | s.requires_arc = true 14 | #s.source_files = 'FFExtension/Classes/*' 15 | 16 | 17 | s.subspec 'Core' do |ss| 18 | ss.source_files = 'FFExtension/Classes/Core/*.{h,m}' 19 | end 20 | 21 | #non_arc_files = 'FFExtension/Classes/MRC/*.{h,m}' 22 | #s.exclude_files = non_arc_files 23 | s.subspec 'MRC' do |sp| 24 | sp.source_files = 'FFExtension/Classes/MRC/*.{h,m}' 25 | sp.requires_arc = false 26 | sp.compiler_flags = '-ObjC' 27 | sp.dependency 'FFExtension/Core' 28 | end 29 | 30 | s.subspec 'Foundation' do |ssp| 31 | ssp.source_files = 'FFExtension/Classes/Foundation/*.{h,m}' 32 | ssp.dependency 'FFExtension/MRC' 33 | ssp.dependency 'FFExtension/Core' 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /FFExtension.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4F4FA85E21549FB400148F47 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F4FA85D21549FB400148F47 /* AppDelegate.m */; }; 11 | 4F4FA86121549FB400148F47 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F4FA86021549FB400148F47 /* ViewController.m */; }; 12 | 4F4FA86421549FB400148F47 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4F4FA86221549FB400148F47 /* Main.storyboard */; }; 13 | 4F4FA86621549FB600148F47 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4F4FA86521549FB600148F47 /* Assets.xcassets */; }; 14 | 4F4FA86921549FB600148F47 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4F4FA86721549FB600148F47 /* LaunchScreen.storyboard */; }; 15 | 4F4FA86C21549FB600148F47 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F4FA86B21549FB600148F47 /* main.m */; }; 16 | 4F4FA87621549FB600148F47 /* FFExtensionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F4FA87521549FB600148F47 /* FFExtensionTests.m */; }; 17 | 4F4FA88121549FB600148F47 /* FFExtensionUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F4FA88021549FB600148F47 /* FFExtensionUITests.m */; }; 18 | 4F4FA8A62154D86500148F47 /* SSZTestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F4FA8A52154D86500148F47 /* SSZTestObject.m */; }; 19 | 4FD280722186EEB80026A1CD /* NSObject+methodSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FD2806E2186EEB80026A1CD /* NSObject+methodSwizzle.m */; }; 20 | 4FD280732186EEB80026A1CD /* FFExceptionProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FD280702186EEB80026A1CD /* FFExceptionProxy.m */; }; 21 | 4FD280872186F3850026A1CD /* NSData+FFExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FD280792186F3850026A1CD /* NSData+FFExtension.m */; }; 22 | 4FD280882186F3850026A1CD /* NSAttributedString+FFExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FD2807A2186F3850026A1CD /* NSAttributedString+FFExtension.m */; }; 23 | 4FD280892186F3850026A1CD /* FFManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FD2807E2186F3850026A1CD /* FFManager.m */; }; 24 | 4FD2808A2186F3850026A1CD /* NSObject+FFExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FD2807F2186F3850026A1CD /* NSObject+FFExtension.m */; }; 25 | 4FD2808B2186F3850026A1CD /* NSDictionary+FFExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FD280802186F3850026A1CD /* NSDictionary+FFExtension.m */; }; 26 | 4FD2808C2186F3850026A1CD /* NSCache+FFExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FD280812186F3850026A1CD /* NSCache+FFExtension.m */; }; 27 | 4FD2808D2186F3850026A1CD /* NSUserDefaults+FFExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FD280832186F3850026A1CD /* NSUserDefaults+FFExtension.m */; }; 28 | 4FD2808E2186F3850026A1CD /* NSString+FFExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FD280842186F3850026A1CD /* NSString+FFExtension.m */; }; 29 | 4FD2808F2186F3850026A1CD /* NSSet+FFExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FD280862186F3850026A1CD /* NSSet+FFExtension.m */; }; 30 | 4FD993E02186E507000096A7 /* NSArray+FFExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FD993DF2186E507000096A7 /* NSArray+FFExtension.m */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXContainerItemProxy section */ 34 | 4F4FA87221549FB600148F47 /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = 4F4FA85121549FB400148F47 /* Project object */; 37 | proxyType = 1; 38 | remoteGlobalIDString = 4F4FA85821549FB400148F47; 39 | remoteInfo = FFExtension; 40 | }; 41 | 4F4FA87D21549FB600148F47 /* PBXContainerItemProxy */ = { 42 | isa = PBXContainerItemProxy; 43 | containerPortal = 4F4FA85121549FB400148F47 /* Project object */; 44 | proxyType = 1; 45 | remoteGlobalIDString = 4F4FA85821549FB400148F47; 46 | remoteInfo = FFExtension; 47 | }; 48 | /* End PBXContainerItemProxy section */ 49 | 50 | /* Begin PBXFileReference section */ 51 | 4F4FA85921549FB400148F47 /* FFExtension.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FFExtension.app; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | 4F4FA85C21549FB400148F47 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 53 | 4F4FA85D21549FB400148F47 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 54 | 4F4FA85F21549FB400148F47 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 55 | 4F4FA86021549FB400148F47 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 56 | 4F4FA86321549FB400148F47 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 57 | 4F4FA86521549FB600148F47 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 58 | 4F4FA86821549FB600148F47 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 59 | 4F4FA86A21549FB600148F47 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 60 | 4F4FA86B21549FB600148F47 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 61 | 4F4FA87121549FB600148F47 /* FFExtensionTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FFExtensionTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 62 | 4F4FA87521549FB600148F47 /* FFExtensionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FFExtensionTests.m; sourceTree = ""; }; 63 | 4F4FA87721549FB600148F47 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 64 | 4F4FA87C21549FB600148F47 /* FFExtensionUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FFExtensionUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | 4F4FA88021549FB600148F47 /* FFExtensionUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FFExtensionUITests.m; sourceTree = ""; }; 66 | 4F4FA88221549FB600148F47 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 67 | 4F4FA8A42154D86500148F47 /* SSZTestObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SSZTestObject.h; sourceTree = ""; }; 68 | 4F4FA8A52154D86500148F47 /* SSZTestObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SSZTestObject.m; sourceTree = ""; }; 69 | 4FD2806D2186EEB80026A1CD /* FFExceptionProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FFExceptionProxy.h; sourceTree = ""; }; 70 | 4FD2806E2186EEB80026A1CD /* NSObject+methodSwizzle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+methodSwizzle.m"; sourceTree = ""; }; 71 | 4FD2806F2186EEB80026A1CD /* FFCommonMacro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FFCommonMacro.h; sourceTree = ""; }; 72 | 4FD280702186EEB80026A1CD /* FFExceptionProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FFExceptionProxy.m; sourceTree = ""; }; 73 | 4FD280712186EEB80026A1CD /* NSObject+methodSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+methodSwizzle.h"; sourceTree = ""; }; 74 | 4FD280752186F3850026A1CD /* NSCache+FFExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSCache+FFExtension.h"; sourceTree = ""; }; 75 | 4FD280762186F3850026A1CD /* NSDictionary+FFExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+FFExtension.h"; sourceTree = ""; }; 76 | 4FD280772186F3850026A1CD /* FFManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FFManager.h; sourceTree = ""; }; 77 | 4FD280782186F3850026A1CD /* NSObject+FFExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+FFExtension.h"; sourceTree = ""; }; 78 | 4FD280792186F3850026A1CD /* NSData+FFExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+FFExtension.m"; sourceTree = ""; }; 79 | 4FD2807A2186F3850026A1CD /* NSAttributedString+FFExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSAttributedString+FFExtension.m"; sourceTree = ""; }; 80 | 4FD2807B2186F3850026A1CD /* NSSet+FFExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSSet+FFExtension.h"; sourceTree = ""; }; 81 | 4FD2807C2186F3850026A1CD /* NSString+FFExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+FFExtension.h"; sourceTree = ""; }; 82 | 4FD2807D2186F3850026A1CD /* NSUserDefaults+FFExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSUserDefaults+FFExtension.h"; sourceTree = ""; }; 83 | 4FD2807E2186F3850026A1CD /* FFManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FFManager.m; sourceTree = ""; }; 84 | 4FD2807F2186F3850026A1CD /* NSObject+FFExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+FFExtension.m"; sourceTree = ""; }; 85 | 4FD280802186F3850026A1CD /* NSDictionary+FFExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+FFExtension.m"; sourceTree = ""; }; 86 | 4FD280812186F3850026A1CD /* NSCache+FFExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSCache+FFExtension.m"; sourceTree = ""; }; 87 | 4FD280822186F3850026A1CD /* NSData+FFExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+FFExtension.h"; sourceTree = ""; }; 88 | 4FD280832186F3850026A1CD /* NSUserDefaults+FFExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSUserDefaults+FFExtension.m"; sourceTree = ""; }; 89 | 4FD280842186F3850026A1CD /* NSString+FFExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+FFExtension.m"; sourceTree = ""; }; 90 | 4FD280852186F3850026A1CD /* NSAttributedString+FFExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSAttributedString+FFExtension.h"; sourceTree = ""; }; 91 | 4FD280862186F3850026A1CD /* NSSet+FFExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSSet+FFExtension.m"; sourceTree = ""; }; 92 | 4FD993DE2186E507000096A7 /* NSArray+FFExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+FFExtension.h"; sourceTree = ""; }; 93 | 4FD993DF2186E507000096A7 /* NSArray+FFExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+FFExtension.m"; sourceTree = ""; }; 94 | /* End PBXFileReference section */ 95 | 96 | /* Begin PBXFrameworksBuildPhase section */ 97 | 4F4FA85621549FB400148F47 /* Frameworks */ = { 98 | isa = PBXFrameworksBuildPhase; 99 | buildActionMask = 2147483647; 100 | files = ( 101 | ); 102 | runOnlyForDeploymentPostprocessing = 0; 103 | }; 104 | 4F4FA86E21549FB600148F47 /* Frameworks */ = { 105 | isa = PBXFrameworksBuildPhase; 106 | buildActionMask = 2147483647; 107 | files = ( 108 | ); 109 | runOnlyForDeploymentPostprocessing = 0; 110 | }; 111 | 4F4FA87921549FB600148F47 /* Frameworks */ = { 112 | isa = PBXFrameworksBuildPhase; 113 | buildActionMask = 2147483647; 114 | files = ( 115 | ); 116 | runOnlyForDeploymentPostprocessing = 0; 117 | }; 118 | /* End PBXFrameworksBuildPhase section */ 119 | 120 | /* Begin PBXGroup section */ 121 | 4F4FA85021549FB400148F47 = { 122 | isa = PBXGroup; 123 | children = ( 124 | 4F4FA85B21549FB400148F47 /* FFExtension */, 125 | 4F4FA87421549FB600148F47 /* FFExtensionTests */, 126 | 4F4FA87F21549FB600148F47 /* FFExtensionUITests */, 127 | 4F4FA85A21549FB400148F47 /* Products */, 128 | ); 129 | sourceTree = ""; 130 | }; 131 | 4F4FA85A21549FB400148F47 /* Products */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 4F4FA85921549FB400148F47 /* FFExtension.app */, 135 | 4F4FA87121549FB600148F47 /* FFExtensionTests.xctest */, 136 | 4F4FA87C21549FB600148F47 /* FFExtensionUITests.xctest */, 137 | ); 138 | name = Products; 139 | sourceTree = ""; 140 | }; 141 | 4F4FA85B21549FB400148F47 /* FFExtension */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | 4F4FA88E21549FDB00148F47 /* Classes */, 145 | 4F4FA85C21549FB400148F47 /* AppDelegate.h */, 146 | 4F4FA85D21549FB400148F47 /* AppDelegate.m */, 147 | 4F4FA85F21549FB400148F47 /* ViewController.h */, 148 | 4F4FA86021549FB400148F47 /* ViewController.m */, 149 | 4F4FA8A42154D86500148F47 /* SSZTestObject.h */, 150 | 4F4FA8A52154D86500148F47 /* SSZTestObject.m */, 151 | 4F4FA86221549FB400148F47 /* Main.storyboard */, 152 | 4F4FA86521549FB600148F47 /* Assets.xcassets */, 153 | 4F4FA86721549FB600148F47 /* LaunchScreen.storyboard */, 154 | 4F4FA86A21549FB600148F47 /* Info.plist */, 155 | 4F4FA86B21549FB600148F47 /* main.m */, 156 | ); 157 | path = FFExtension; 158 | sourceTree = ""; 159 | }; 160 | 4F4FA87421549FB600148F47 /* FFExtensionTests */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | 4F4FA87521549FB600148F47 /* FFExtensionTests.m */, 164 | 4F4FA87721549FB600148F47 /* Info.plist */, 165 | ); 166 | path = FFExtensionTests; 167 | sourceTree = ""; 168 | }; 169 | 4F4FA87F21549FB600148F47 /* FFExtensionUITests */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | 4F4FA88021549FB600148F47 /* FFExtensionUITests.m */, 173 | 4F4FA88221549FB600148F47 /* Info.plist */, 174 | ); 175 | path = FFExtensionUITests; 176 | sourceTree = ""; 177 | }; 178 | 4F4FA88E21549FDB00148F47 /* Classes */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | 4FD280742186F3850026A1CD /* Foundation */, 182 | 4FD2806C2186EEB80026A1CD /* Core */, 183 | 4FD993DD2186E507000096A7 /* MRC */, 184 | ); 185 | path = Classes; 186 | sourceTree = ""; 187 | }; 188 | 4FD2806C2186EEB80026A1CD /* Core */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | 4FD2806F2186EEB80026A1CD /* FFCommonMacro.h */, 192 | 4FD2806D2186EEB80026A1CD /* FFExceptionProxy.h */, 193 | 4FD280702186EEB80026A1CD /* FFExceptionProxy.m */, 194 | 4FD280712186EEB80026A1CD /* NSObject+methodSwizzle.h */, 195 | 4FD2806E2186EEB80026A1CD /* NSObject+methodSwizzle.m */, 196 | ); 197 | path = Core; 198 | sourceTree = ""; 199 | }; 200 | 4FD280742186F3850026A1CD /* Foundation */ = { 201 | isa = PBXGroup; 202 | children = ( 203 | 4FD280772186F3850026A1CD /* FFManager.h */, 204 | 4FD2807E2186F3850026A1CD /* FFManager.m */, 205 | 4FD280782186F3850026A1CD /* NSObject+FFExtension.h */, 206 | 4FD2807F2186F3850026A1CD /* NSObject+FFExtension.m */, 207 | 4FD2807B2186F3850026A1CD /* NSSet+FFExtension.h */, 208 | 4FD280862186F3850026A1CD /* NSSet+FFExtension.m */, 209 | 4FD2807C2186F3850026A1CD /* NSString+FFExtension.h */, 210 | 4FD280842186F3850026A1CD /* NSString+FFExtension.m */, 211 | 4FD280762186F3850026A1CD /* NSDictionary+FFExtension.h */, 212 | 4FD280802186F3850026A1CD /* NSDictionary+FFExtension.m */, 213 | 4FD280752186F3850026A1CD /* NSCache+FFExtension.h */, 214 | 4FD280812186F3850026A1CD /* NSCache+FFExtension.m */, 215 | 4FD280822186F3850026A1CD /* NSData+FFExtension.h */, 216 | 4FD280792186F3850026A1CD /* NSData+FFExtension.m */, 217 | 4FD2807D2186F3850026A1CD /* NSUserDefaults+FFExtension.h */, 218 | 4FD280832186F3850026A1CD /* NSUserDefaults+FFExtension.m */, 219 | 4FD280852186F3850026A1CD /* NSAttributedString+FFExtension.h */, 220 | 4FD2807A2186F3850026A1CD /* NSAttributedString+FFExtension.m */, 221 | ); 222 | path = Foundation; 223 | sourceTree = ""; 224 | }; 225 | 4FD993DD2186E507000096A7 /* MRC */ = { 226 | isa = PBXGroup; 227 | children = ( 228 | 4FD993DE2186E507000096A7 /* NSArray+FFExtension.h */, 229 | 4FD993DF2186E507000096A7 /* NSArray+FFExtension.m */, 230 | ); 231 | path = MRC; 232 | sourceTree = ""; 233 | }; 234 | /* End PBXGroup section */ 235 | 236 | /* Begin PBXNativeTarget section */ 237 | 4F4FA85821549FB400148F47 /* FFExtension */ = { 238 | isa = PBXNativeTarget; 239 | buildConfigurationList = 4F4FA88521549FB600148F47 /* Build configuration list for PBXNativeTarget "FFExtension" */; 240 | buildPhases = ( 241 | 4F4FA85521549FB400148F47 /* Sources */, 242 | 4F4FA85621549FB400148F47 /* Frameworks */, 243 | 4F4FA85721549FB400148F47 /* Resources */, 244 | ); 245 | buildRules = ( 246 | ); 247 | dependencies = ( 248 | ); 249 | name = FFExtension; 250 | productName = FFExtension; 251 | productReference = 4F4FA85921549FB400148F47 /* FFExtension.app */; 252 | productType = "com.apple.product-type.application"; 253 | }; 254 | 4F4FA87021549FB600148F47 /* FFExtensionTests */ = { 255 | isa = PBXNativeTarget; 256 | buildConfigurationList = 4F4FA88821549FB600148F47 /* Build configuration list for PBXNativeTarget "FFExtensionTests" */; 257 | buildPhases = ( 258 | 4F4FA86D21549FB600148F47 /* Sources */, 259 | 4F4FA86E21549FB600148F47 /* Frameworks */, 260 | 4F4FA86F21549FB600148F47 /* Resources */, 261 | ); 262 | buildRules = ( 263 | ); 264 | dependencies = ( 265 | 4F4FA87321549FB600148F47 /* PBXTargetDependency */, 266 | ); 267 | name = FFExtensionTests; 268 | productName = FFExtensionTests; 269 | productReference = 4F4FA87121549FB600148F47 /* FFExtensionTests.xctest */; 270 | productType = "com.apple.product-type.bundle.unit-test"; 271 | }; 272 | 4F4FA87B21549FB600148F47 /* FFExtensionUITests */ = { 273 | isa = PBXNativeTarget; 274 | buildConfigurationList = 4F4FA88B21549FB600148F47 /* Build configuration list for PBXNativeTarget "FFExtensionUITests" */; 275 | buildPhases = ( 276 | 4F4FA87821549FB600148F47 /* Sources */, 277 | 4F4FA87921549FB600148F47 /* Frameworks */, 278 | 4F4FA87A21549FB600148F47 /* Resources */, 279 | ); 280 | buildRules = ( 281 | ); 282 | dependencies = ( 283 | 4F4FA87E21549FB600148F47 /* PBXTargetDependency */, 284 | ); 285 | name = FFExtensionUITests; 286 | productName = FFExtensionUITests; 287 | productReference = 4F4FA87C21549FB600148F47 /* FFExtensionUITests.xctest */; 288 | productType = "com.apple.product-type.bundle.ui-testing"; 289 | }; 290 | /* End PBXNativeTarget section */ 291 | 292 | /* Begin PBXProject section */ 293 | 4F4FA85121549FB400148F47 /* Project object */ = { 294 | isa = PBXProject; 295 | attributes = { 296 | LastUpgradeCheck = 0940; 297 | ORGANIZATIONNAME = shensz; 298 | TargetAttributes = { 299 | 4F4FA85821549FB400148F47 = { 300 | CreatedOnToolsVersion = 9.4; 301 | }; 302 | 4F4FA87021549FB600148F47 = { 303 | CreatedOnToolsVersion = 9.4; 304 | TestTargetID = 4F4FA85821549FB400148F47; 305 | }; 306 | 4F4FA87B21549FB600148F47 = { 307 | CreatedOnToolsVersion = 9.4; 308 | TestTargetID = 4F4FA85821549FB400148F47; 309 | }; 310 | }; 311 | }; 312 | buildConfigurationList = 4F4FA85421549FB400148F47 /* Build configuration list for PBXProject "FFExtension" */; 313 | compatibilityVersion = "Xcode 9.3"; 314 | developmentRegion = en; 315 | hasScannedForEncodings = 0; 316 | knownRegions = ( 317 | en, 318 | Base, 319 | ); 320 | mainGroup = 4F4FA85021549FB400148F47; 321 | productRefGroup = 4F4FA85A21549FB400148F47 /* Products */; 322 | projectDirPath = ""; 323 | projectRoot = ""; 324 | targets = ( 325 | 4F4FA85821549FB400148F47 /* FFExtension */, 326 | 4F4FA87021549FB600148F47 /* FFExtensionTests */, 327 | 4F4FA87B21549FB600148F47 /* FFExtensionUITests */, 328 | ); 329 | }; 330 | /* End PBXProject section */ 331 | 332 | /* Begin PBXResourcesBuildPhase section */ 333 | 4F4FA85721549FB400148F47 /* Resources */ = { 334 | isa = PBXResourcesBuildPhase; 335 | buildActionMask = 2147483647; 336 | files = ( 337 | 4F4FA86921549FB600148F47 /* LaunchScreen.storyboard in Resources */, 338 | 4F4FA86621549FB600148F47 /* Assets.xcassets in Resources */, 339 | 4F4FA86421549FB400148F47 /* Main.storyboard in Resources */, 340 | ); 341 | runOnlyForDeploymentPostprocessing = 0; 342 | }; 343 | 4F4FA86F21549FB600148F47 /* Resources */ = { 344 | isa = PBXResourcesBuildPhase; 345 | buildActionMask = 2147483647; 346 | files = ( 347 | ); 348 | runOnlyForDeploymentPostprocessing = 0; 349 | }; 350 | 4F4FA87A21549FB600148F47 /* Resources */ = { 351 | isa = PBXResourcesBuildPhase; 352 | buildActionMask = 2147483647; 353 | files = ( 354 | ); 355 | runOnlyForDeploymentPostprocessing = 0; 356 | }; 357 | /* End PBXResourcesBuildPhase section */ 358 | 359 | /* Begin PBXSourcesBuildPhase section */ 360 | 4F4FA85521549FB400148F47 /* Sources */ = { 361 | isa = PBXSourcesBuildPhase; 362 | buildActionMask = 2147483647; 363 | files = ( 364 | 4F4FA86121549FB400148F47 /* ViewController.m in Sources */, 365 | 4FD2808C2186F3850026A1CD /* NSCache+FFExtension.m in Sources */, 366 | 4FD280722186EEB80026A1CD /* NSObject+methodSwizzle.m in Sources */, 367 | 4FD993E02186E507000096A7 /* NSArray+FFExtension.m in Sources */, 368 | 4FD280892186F3850026A1CD /* FFManager.m in Sources */, 369 | 4FD2808E2186F3850026A1CD /* NSString+FFExtension.m in Sources */, 370 | 4F4FA86C21549FB600148F47 /* main.m in Sources */, 371 | 4F4FA8A62154D86500148F47 /* SSZTestObject.m in Sources */, 372 | 4F4FA85E21549FB400148F47 /* AppDelegate.m in Sources */, 373 | 4FD280882186F3850026A1CD /* NSAttributedString+FFExtension.m in Sources */, 374 | 4FD2808D2186F3850026A1CD /* NSUserDefaults+FFExtension.m in Sources */, 375 | 4FD2808B2186F3850026A1CD /* NSDictionary+FFExtension.m in Sources */, 376 | 4FD280872186F3850026A1CD /* NSData+FFExtension.m in Sources */, 377 | 4FD2808A2186F3850026A1CD /* NSObject+FFExtension.m in Sources */, 378 | 4FD280732186EEB80026A1CD /* FFExceptionProxy.m in Sources */, 379 | 4FD2808F2186F3850026A1CD /* NSSet+FFExtension.m in Sources */, 380 | ); 381 | runOnlyForDeploymentPostprocessing = 0; 382 | }; 383 | 4F4FA86D21549FB600148F47 /* Sources */ = { 384 | isa = PBXSourcesBuildPhase; 385 | buildActionMask = 2147483647; 386 | files = ( 387 | 4F4FA87621549FB600148F47 /* FFExtensionTests.m in Sources */, 388 | ); 389 | runOnlyForDeploymentPostprocessing = 0; 390 | }; 391 | 4F4FA87821549FB600148F47 /* Sources */ = { 392 | isa = PBXSourcesBuildPhase; 393 | buildActionMask = 2147483647; 394 | files = ( 395 | 4F4FA88121549FB600148F47 /* FFExtensionUITests.m in Sources */, 396 | ); 397 | runOnlyForDeploymentPostprocessing = 0; 398 | }; 399 | /* End PBXSourcesBuildPhase section */ 400 | 401 | /* Begin PBXTargetDependency section */ 402 | 4F4FA87321549FB600148F47 /* PBXTargetDependency */ = { 403 | isa = PBXTargetDependency; 404 | target = 4F4FA85821549FB400148F47 /* FFExtension */; 405 | targetProxy = 4F4FA87221549FB600148F47 /* PBXContainerItemProxy */; 406 | }; 407 | 4F4FA87E21549FB600148F47 /* PBXTargetDependency */ = { 408 | isa = PBXTargetDependency; 409 | target = 4F4FA85821549FB400148F47 /* FFExtension */; 410 | targetProxy = 4F4FA87D21549FB600148F47 /* PBXContainerItemProxy */; 411 | }; 412 | /* End PBXTargetDependency section */ 413 | 414 | /* Begin PBXVariantGroup section */ 415 | 4F4FA86221549FB400148F47 /* Main.storyboard */ = { 416 | isa = PBXVariantGroup; 417 | children = ( 418 | 4F4FA86321549FB400148F47 /* Base */, 419 | ); 420 | name = Main.storyboard; 421 | sourceTree = ""; 422 | }; 423 | 4F4FA86721549FB600148F47 /* LaunchScreen.storyboard */ = { 424 | isa = PBXVariantGroup; 425 | children = ( 426 | 4F4FA86821549FB600148F47 /* Base */, 427 | ); 428 | name = LaunchScreen.storyboard; 429 | sourceTree = ""; 430 | }; 431 | /* End PBXVariantGroup section */ 432 | 433 | /* Begin XCBuildConfiguration section */ 434 | 4F4FA88321549FB600148F47 /* Debug */ = { 435 | isa = XCBuildConfiguration; 436 | buildSettings = { 437 | ALWAYS_SEARCH_USER_PATHS = NO; 438 | CLANG_ANALYZER_NONNULL = YES; 439 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 440 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 441 | CLANG_CXX_LIBRARY = "libc++"; 442 | CLANG_ENABLE_MODULES = YES; 443 | CLANG_ENABLE_OBJC_ARC = YES; 444 | CLANG_ENABLE_OBJC_WEAK = YES; 445 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 446 | CLANG_WARN_BOOL_CONVERSION = YES; 447 | CLANG_WARN_COMMA = YES; 448 | CLANG_WARN_CONSTANT_CONVERSION = YES; 449 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 450 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 451 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 452 | CLANG_WARN_EMPTY_BODY = YES; 453 | CLANG_WARN_ENUM_CONVERSION = YES; 454 | CLANG_WARN_INFINITE_RECURSION = YES; 455 | CLANG_WARN_INT_CONVERSION = YES; 456 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 457 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 458 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 459 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 460 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 461 | CLANG_WARN_STRICT_PROTOTYPES = YES; 462 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 463 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 464 | CLANG_WARN_UNREACHABLE_CODE = YES; 465 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 466 | CODE_SIGN_IDENTITY = "iPhone Developer"; 467 | COPY_PHASE_STRIP = NO; 468 | DEBUG_INFORMATION_FORMAT = dwarf; 469 | ENABLE_STRICT_OBJC_MSGSEND = YES; 470 | ENABLE_TESTABILITY = YES; 471 | GCC_C_LANGUAGE_STANDARD = gnu11; 472 | GCC_DYNAMIC_NO_PIC = NO; 473 | GCC_NO_COMMON_BLOCKS = YES; 474 | GCC_OPTIMIZATION_LEVEL = 0; 475 | GCC_PREPROCESSOR_DEFINITIONS = ( 476 | "DEBUG=1", 477 | "$(inherited)", 478 | ); 479 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 480 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 481 | GCC_WARN_UNDECLARED_SELECTOR = YES; 482 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 483 | GCC_WARN_UNUSED_FUNCTION = YES; 484 | GCC_WARN_UNUSED_VARIABLE = YES; 485 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 486 | MTL_ENABLE_DEBUG_INFO = YES; 487 | ONLY_ACTIVE_ARCH = YES; 488 | SDKROOT = iphoneos; 489 | }; 490 | name = Debug; 491 | }; 492 | 4F4FA88421549FB600148F47 /* Release */ = { 493 | isa = XCBuildConfiguration; 494 | buildSettings = { 495 | ALWAYS_SEARCH_USER_PATHS = NO; 496 | CLANG_ANALYZER_NONNULL = YES; 497 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 498 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 499 | CLANG_CXX_LIBRARY = "libc++"; 500 | CLANG_ENABLE_MODULES = YES; 501 | CLANG_ENABLE_OBJC_ARC = YES; 502 | CLANG_ENABLE_OBJC_WEAK = YES; 503 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 504 | CLANG_WARN_BOOL_CONVERSION = YES; 505 | CLANG_WARN_COMMA = YES; 506 | CLANG_WARN_CONSTANT_CONVERSION = YES; 507 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 508 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 509 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 510 | CLANG_WARN_EMPTY_BODY = YES; 511 | CLANG_WARN_ENUM_CONVERSION = YES; 512 | CLANG_WARN_INFINITE_RECURSION = YES; 513 | CLANG_WARN_INT_CONVERSION = YES; 514 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 515 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 516 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 517 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 518 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 519 | CLANG_WARN_STRICT_PROTOTYPES = YES; 520 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 521 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 522 | CLANG_WARN_UNREACHABLE_CODE = YES; 523 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 524 | CODE_SIGN_IDENTITY = "iPhone Developer"; 525 | COPY_PHASE_STRIP = NO; 526 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 527 | ENABLE_NS_ASSERTIONS = NO; 528 | ENABLE_STRICT_OBJC_MSGSEND = YES; 529 | GCC_C_LANGUAGE_STANDARD = gnu11; 530 | GCC_NO_COMMON_BLOCKS = YES; 531 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 532 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 533 | GCC_WARN_UNDECLARED_SELECTOR = YES; 534 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 535 | GCC_WARN_UNUSED_FUNCTION = YES; 536 | GCC_WARN_UNUSED_VARIABLE = YES; 537 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 538 | MTL_ENABLE_DEBUG_INFO = NO; 539 | SDKROOT = iphoneos; 540 | VALIDATE_PRODUCT = YES; 541 | }; 542 | name = Release; 543 | }; 544 | 4F4FA88621549FB600148F47 /* Debug */ = { 545 | isa = XCBuildConfiguration; 546 | buildSettings = { 547 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 548 | CODE_SIGN_STYLE = Automatic; 549 | DEVELOPMENT_TEAM = W9AD932V2V; 550 | INFOPLIST_FILE = FFExtension/Info.plist; 551 | LD_RUNPATH_SEARCH_PATHS = ( 552 | "$(inherited)", 553 | "@executable_path/Frameworks", 554 | ); 555 | PRODUCT_BUNDLE_IDENTIFIER = shensz.FFExtension; 556 | PRODUCT_NAME = "$(TARGET_NAME)"; 557 | TARGETED_DEVICE_FAMILY = "1,2"; 558 | }; 559 | name = Debug; 560 | }; 561 | 4F4FA88721549FB600148F47 /* Release */ = { 562 | isa = XCBuildConfiguration; 563 | buildSettings = { 564 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 565 | CODE_SIGN_STYLE = Automatic; 566 | DEVELOPMENT_TEAM = W9AD932V2V; 567 | INFOPLIST_FILE = FFExtension/Info.plist; 568 | LD_RUNPATH_SEARCH_PATHS = ( 569 | "$(inherited)", 570 | "@executable_path/Frameworks", 571 | ); 572 | PRODUCT_BUNDLE_IDENTIFIER = shensz.FFExtension; 573 | PRODUCT_NAME = "$(TARGET_NAME)"; 574 | TARGETED_DEVICE_FAMILY = "1,2"; 575 | }; 576 | name = Release; 577 | }; 578 | 4F4FA88921549FB600148F47 /* Debug */ = { 579 | isa = XCBuildConfiguration; 580 | buildSettings = { 581 | BUNDLE_LOADER = "$(TEST_HOST)"; 582 | CODE_SIGN_STYLE = Automatic; 583 | DEVELOPMENT_TEAM = 6L3KQW29EM; 584 | INFOPLIST_FILE = FFExtensionTests/Info.plist; 585 | LD_RUNPATH_SEARCH_PATHS = ( 586 | "$(inherited)", 587 | "@executable_path/Frameworks", 588 | "@loader_path/Frameworks", 589 | ); 590 | PRODUCT_BUNDLE_IDENTIFIER = shensz.FFExtensionTests; 591 | PRODUCT_NAME = "$(TARGET_NAME)"; 592 | TARGETED_DEVICE_FAMILY = "1,2"; 593 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FFExtension.app/FFExtension"; 594 | }; 595 | name = Debug; 596 | }; 597 | 4F4FA88A21549FB600148F47 /* Release */ = { 598 | isa = XCBuildConfiguration; 599 | buildSettings = { 600 | BUNDLE_LOADER = "$(TEST_HOST)"; 601 | CODE_SIGN_STYLE = Automatic; 602 | DEVELOPMENT_TEAM = 6L3KQW29EM; 603 | INFOPLIST_FILE = FFExtensionTests/Info.plist; 604 | LD_RUNPATH_SEARCH_PATHS = ( 605 | "$(inherited)", 606 | "@executable_path/Frameworks", 607 | "@loader_path/Frameworks", 608 | ); 609 | PRODUCT_BUNDLE_IDENTIFIER = shensz.FFExtensionTests; 610 | PRODUCT_NAME = "$(TARGET_NAME)"; 611 | TARGETED_DEVICE_FAMILY = "1,2"; 612 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FFExtension.app/FFExtension"; 613 | }; 614 | name = Release; 615 | }; 616 | 4F4FA88C21549FB600148F47 /* Debug */ = { 617 | isa = XCBuildConfiguration; 618 | buildSettings = { 619 | CODE_SIGN_STYLE = Automatic; 620 | DEVELOPMENT_TEAM = 6L3KQW29EM; 621 | INFOPLIST_FILE = FFExtensionUITests/Info.plist; 622 | LD_RUNPATH_SEARCH_PATHS = ( 623 | "$(inherited)", 624 | "@executable_path/Frameworks", 625 | "@loader_path/Frameworks", 626 | ); 627 | PRODUCT_BUNDLE_IDENTIFIER = shensz.FFExtensionUITests; 628 | PRODUCT_NAME = "$(TARGET_NAME)"; 629 | TARGETED_DEVICE_FAMILY = "1,2"; 630 | TEST_TARGET_NAME = FFExtension; 631 | }; 632 | name = Debug; 633 | }; 634 | 4F4FA88D21549FB600148F47 /* Release */ = { 635 | isa = XCBuildConfiguration; 636 | buildSettings = { 637 | CODE_SIGN_STYLE = Automatic; 638 | DEVELOPMENT_TEAM = 6L3KQW29EM; 639 | INFOPLIST_FILE = FFExtensionUITests/Info.plist; 640 | LD_RUNPATH_SEARCH_PATHS = ( 641 | "$(inherited)", 642 | "@executable_path/Frameworks", 643 | "@loader_path/Frameworks", 644 | ); 645 | PRODUCT_BUNDLE_IDENTIFIER = shensz.FFExtensionUITests; 646 | PRODUCT_NAME = "$(TARGET_NAME)"; 647 | TARGETED_DEVICE_FAMILY = "1,2"; 648 | TEST_TARGET_NAME = FFExtension; 649 | }; 650 | name = Release; 651 | }; 652 | /* End XCBuildConfiguration section */ 653 | 654 | /* Begin XCConfigurationList section */ 655 | 4F4FA85421549FB400148F47 /* Build configuration list for PBXProject "FFExtension" */ = { 656 | isa = XCConfigurationList; 657 | buildConfigurations = ( 658 | 4F4FA88321549FB600148F47 /* Debug */, 659 | 4F4FA88421549FB600148F47 /* Release */, 660 | ); 661 | defaultConfigurationIsVisible = 0; 662 | defaultConfigurationName = Release; 663 | }; 664 | 4F4FA88521549FB600148F47 /* Build configuration list for PBXNativeTarget "FFExtension" */ = { 665 | isa = XCConfigurationList; 666 | buildConfigurations = ( 667 | 4F4FA88621549FB600148F47 /* Debug */, 668 | 4F4FA88721549FB600148F47 /* Release */, 669 | ); 670 | defaultConfigurationIsVisible = 0; 671 | defaultConfigurationName = Release; 672 | }; 673 | 4F4FA88821549FB600148F47 /* Build configuration list for PBXNativeTarget "FFExtensionTests" */ = { 674 | isa = XCConfigurationList; 675 | buildConfigurations = ( 676 | 4F4FA88921549FB600148F47 /* Debug */, 677 | 4F4FA88A21549FB600148F47 /* Release */, 678 | ); 679 | defaultConfigurationIsVisible = 0; 680 | defaultConfigurationName = Release; 681 | }; 682 | 4F4FA88B21549FB600148F47 /* Build configuration list for PBXNativeTarget "FFExtensionUITests" */ = { 683 | isa = XCConfigurationList; 684 | buildConfigurations = ( 685 | 4F4FA88C21549FB600148F47 /* Debug */, 686 | 4F4FA88D21549FB600148F47 /* Release */, 687 | ); 688 | defaultConfigurationIsVisible = 0; 689 | defaultConfigurationName = Release; 690 | }; 691 | /* End XCConfigurationList section */ 692 | }; 693 | rootObject = 4F4FA85121549FB400148F47 /* Project object */; 694 | } 695 | -------------------------------------------------------------------------------- /FFExtension.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FFExtension.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /FFExtension.xcodeproj/xcuserdata/hufeng.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 18 | 30 | 31 | 32 | 34 | 46 | 47 | 48 | 50 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /FFExtension.xcodeproj/xcuserdata/hufeng.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | FFExtension.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | FFExtension.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /FFExtension/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. 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 | -------------------------------------------------------------------------------- /FFExtension/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "FFManager.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | NSArray *arr = @[@"SSZ"]; 22 | 23 | [[FFManager sharedInstance] startWorkWithOption:FFHookOptionAll unrecogziedSelectorClassPrefixs:arr callBackBlock:^(NSDictionary *exceptionDic) { 24 | // [exceptionDic objectForKey:FF_Name] 25 | // NSLog(@"exceptionDic = %@", exceptionDic); 26 | // NSArray *callBacks = [exceptionDic objectForKey:FF_CallStackSymbols]; 27 | // NSArray *serialCallBacks = [self sortStackArray:callBacks]; 28 | // NSLog(@"result = %@", serialCallBacks); 29 | 30 | 31 | }]; 32 | // [FFManager startWorkWithOption:FFHookOptionAll unrecogziedSelectorClassPrefixs:arr]; 33 | 34 | return YES; 35 | } 36 | 37 | 38 | - (void)applicationWillResignActive:(UIApplication *)application { 39 | // 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. 40 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 41 | } 42 | 43 | 44 | - (void)applicationDidEnterBackground:(UIApplication *)application { 45 | // 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. 46 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 47 | } 48 | 49 | 50 | - (void)applicationWillEnterForeground:(UIApplication *)application { 51 | // 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. 52 | } 53 | 54 | 55 | - (void)applicationDidBecomeActive:(UIApplication *)application { 56 | // 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. 57 | } 58 | 59 | 60 | - (void)applicationWillTerminate:(UIApplication *)application { 61 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 62 | } 63 | 64 | - (NSArray *)sortStackArray:(NSArray *)callStacks 65 | { 66 | @autoreleasepool { 67 | NSMutableArray *results = [NSMutableArray array]; 68 | for (NSString *stack in callStacks) { 69 | NSArray *tempArray = [stack componentsSeparatedByString:@" "]; 70 | NSMutableString *mutableStr = [NSMutableString string]; 71 | int i = 0; 72 | for (NSString *tempStr in tempArray) { 73 | if (![tempStr hasPrefix:@"0x"] && i > 0) { 74 | [mutableStr appendFormat:@"%@ ", tempStr]; 75 | } 76 | i++; 77 | } 78 | [results addObject:mutableStr]; 79 | } 80 | return results; 81 | } 82 | } 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /FFExtension/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 | } -------------------------------------------------------------------------------- /FFExtension/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /FFExtension/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /FFExtension/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 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /FFExtension/Classes/Core/FFCommonMacro.h: -------------------------------------------------------------------------------- 1 | // 2 | // FFCommonMacro.h 3 | // FFExtension 4 | // 5 | // Created by hufeng on 8/10/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #ifndef FFCommonMacro_h 10 | #define FFCommonMacro_h 11 | 12 | #define FF_Reason @"reason" 13 | #define FF_Name @"name" 14 | #define FF_ExtraDic @"extraDic" 15 | #define FF_CallStackSymbols @"callStackSymbols" 16 | #define FF_Prefix @"FFExtension " 17 | 18 | #endif /* FFCommonMacro_h */ 19 | -------------------------------------------------------------------------------- /FFExtension/Classes/Core/FFExceptionProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // FFExceptionProxy.h 3 | // FFExtension 4 | // 5 | // Created by hufeng on 26/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol FFExceptionDelegate 12 | - (void)ff_captureExceptionWithErrorDic:(NSDictionary *)errorDic; 13 | @end 14 | 15 | @interface FFExceptionProxy : NSObject 16 | 17 | + (instancetype)sharedInstance; 18 | 19 | @property (nonatomic, weak) id delegate; 20 | 21 | - (void)reportExceptionWithMessage:(NSString *)message extraDic:(NSDictionary *)extraDic; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /FFExtension/Classes/Core/FFExceptionProxy.m: -------------------------------------------------------------------------------- 1 | // 2 | // FFExceptionProxy.m 3 | // FFExtension 4 | // 5 | // Created by hufeng on 26/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import "FFExceptionProxy.h" 10 | #import "FFCommonMacro.h" 11 | 12 | @implementation FFExceptionProxy 13 | 14 | + (instancetype)sharedInstance 15 | { 16 | static FFExceptionProxy *g_instance = nil; 17 | static dispatch_once_t onceToken; 18 | dispatch_once(&onceToken, ^{ 19 | g_instance = [[self alloc] init]; 20 | }); 21 | 22 | return g_instance; 23 | } 24 | 25 | - (void)reportExceptionWithMessage:(NSString *)message extraDic:(NSDictionary *)extraDic 26 | { 27 | NSString *msg = [FF_Prefix stringByAppendingString:message]; 28 | NSLog(@"%@", msg); 29 | NSArray *allThreads = [NSThread callStackSymbols]; 30 | 31 | NSDictionary *errorDic = @{ 32 | FF_Name : @"FFExtension capture's Exception", 33 | FF_Reason : msg?:@"", 34 | FF_ExtraDic : extraDic?:@{}, 35 | FF_CallStackSymbols : allThreads?:@[], 36 | }; 37 | if ([self.delegate respondsToSelector:@selector(ff_captureExceptionWithErrorDic:)]) { 38 | [self.delegate ff_captureExceptionWithErrorDic:errorDic]; 39 | } 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /FFExtension/Classes/Core/NSObject+methodSwizzle.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+methodSwizzle.h 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSObject (methodSwizzle) 12 | 13 | + (void)ff_classSwizzleWithClass:(Class)class originSelector:(SEL)originSelector swizzleSelector:(SEL)swizzleSelector; 14 | 15 | + (void)ff_instancenSwizzleWithClass:(Class)class originSelector:(SEL)originSelector swizzleSelector:(SEL)swizzleSelector; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /FFExtension/Classes/Core/NSObject+methodSwizzle.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+methodSwizzle.m 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import "NSObject+methodSwizzle.h" 10 | #import 11 | 12 | 13 | @implementation NSObject (methodSwizzle) 14 | 15 | + (void)ff_classSwizzleWithClass:(Class)class originSelector:(SEL)originSelector swizzleSelector:(SEL)swizzleSelector 16 | { 17 | Class realClass = object_getClass(class); 18 | Method originMethod = class_getClassMethod(realClass, originSelector); 19 | Method swizzleMethod = class_getClassMethod(realClass, swizzleSelector); 20 | if (!originMethod || !swizzleMethod) { 21 | return; 22 | } 23 | 24 | class_addMethod(realClass, 25 | originSelector, 26 | method_getImplementation(originMethod), 27 | method_getTypeEncoding(originMethod)); 28 | class_addMethod(realClass, 29 | swizzleSelector, 30 | method_getImplementation(swizzleMethod), 31 | method_getTypeEncoding(swizzleMethod)); 32 | 33 | Method originMethod2 = class_getClassMethod(class, originSelector); 34 | Method swizzleMethod2 = class_getClassMethod(class, swizzleSelector); 35 | method_exchangeImplementations(originMethod2, swizzleMethod2); 36 | } 37 | 38 | + (void)ff_instancenSwizzleWithClass:(Class)class originSelector:(SEL)originSelector swizzleSelector:(SEL)swizzleSelector 39 | { 40 | Method originMethod = class_getInstanceMethod(class, originSelector); 41 | Method swizzleMethod = class_getInstanceMethod(class, swizzleSelector); 42 | if (!originMethod || !swizzleMethod) { 43 | return; 44 | } 45 | 46 | class_addMethod(class, 47 | originSelector, 48 | method_getImplementation(originMethod), 49 | method_getTypeEncoding(originMethod)); 50 | class_addMethod(class, 51 | swizzleSelector, 52 | method_getImplementation(swizzleMethod), 53 | method_getTypeEncoding(swizzleMethod)); 54 | 55 | 56 | ///< 添加完了之后要重新赋值,因为原来的两个method都是父类的。参考自见https://github.com/rentzsch/jrswizzle/blob/semver-1.x/JRSwizzle.m 57 | Method originMethod2 = class_getInstanceMethod(class, originSelector); 58 | Method swizzleMethod2 = class_getInstanceMethod(class, swizzleSelector); 59 | 60 | method_exchangeImplementations(originMethod2, swizzleMethod2); 61 | 62 | 63 | ///< 第二种方案也OK 64 | /* 65 | BOOL success = class_addMethod(class, 66 | originSelector, 67 | method_getImplementation(swizzleMethod), 68 | method_getTypeEncoding(swizzleMethod)); 69 | if (success) { 70 | class_replaceMethod(class, 71 | swizzleSelector, 72 | method_getImplementation(originMethod), 73 | method_getTypeEncoding(originMethod)); 74 | } else { 75 | method_exchangeImplementations(originMethod, swizzleMethod); 76 | } 77 | */ 78 | } 79 | 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/FFManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // FFManager.h 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "FFCommonMacro.h" 11 | 12 | typedef NS_OPTIONS(NSUInteger, FFHookOption){ 13 | FFHookOptionNone = 0, 14 | FFHookOptionUnrecognizedSelector = 1 << 0, 15 | FFHookOptionNSString = 1 << 1, 16 | FFHookOptionNSArray = 1 << 2, 17 | FFHookOptionNSDictionary = 1 << 3, 18 | // FFHookOptionNSData = 1 << 4, ///< 会跟高德SDK的加解密函数冲突,暂时无法解决,先去掉 19 | FFHookOptionNSSet = 1 << 5, 20 | FFHookOptionNSUserDefaults = 1 << 6, 21 | FFHookOptionNSCache = 1 << 7, 22 | FFHookOptionNSAttributedString = 1 << 8, 23 | FFHookOptionAll = 0xFFFF, 24 | }; 25 | 26 | typedef void(^FFExceptionBlock)(NSDictionary *exceptionDic); 27 | 28 | @interface FFManager : NSObject 29 | 30 | + (instancetype)sharedInstance; 31 | 32 | 33 | - (void)startWorkWithOption:(FFHookOption)option callBackBlock:(FFExceptionBlock)block; 34 | 35 | /** 36 | you must call one of the method to start work. 37 | 38 | @param option - type of hook 39 | @param classPrefixs - for your own class(Case Sensitive), add unrecognized selector sent to instance exception hook, default is nil. 40 | @param block - callBack to upload exception logs to server 41 | */ 42 | - (void)startWorkWithOption:(FFHookOption)option unrecogziedSelectorClassPrefixs:(NSArray *)classPrefixs callBackBlock:(FFExceptionBlock)block; 43 | 44 | 45 | /** 46 | 用来更新数组的接口,方便用接口下发数据来配置,不用发版也能避免某些崩溃 47 | 48 | @param classPrefixs 需要防护的类名前缀数组,内部会去重 49 | */ 50 | - (void)updateUnrecogziedSelectorClassPrefixs:(NSArray *)classPrefixs; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/FFManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // FFManager.m 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import "FFManager.h" 10 | #import "NSString+FFExtension.h" 11 | #import "NSArray+FFExtension.h" 12 | #import "NSDictionary+FFExtension.h" 13 | #import "NSObject+FFExtension.h" 14 | #import "NSData+FFExtension.h" 15 | #import "NSSet+FFExtension.h" 16 | #import "NSUserDefaults+FFExtension.h" 17 | #import "NSCache+FFExtension.h" 18 | #import "NSAttributedString+FFExtension.h" 19 | #import "FFExceptionProxy.h" 20 | 21 | @interface FFManager () 22 | 23 | @property (nonatomic, copy) FFExceptionBlock callBackBlock; 24 | 25 | @end 26 | 27 | @implementation FFManager 28 | 29 | + (instancetype)sharedInstance 30 | { 31 | static FFManager *g_instance = nil; 32 | static dispatch_once_t onceToken; 33 | dispatch_once(&onceToken, ^{ 34 | g_instance = [[self alloc] init]; 35 | }); 36 | 37 | return g_instance; 38 | } 39 | 40 | - (instancetype)init 41 | { 42 | self = [super init]; 43 | if (self) { 44 | [FFExceptionProxy sharedInstance].delegate = self; 45 | } 46 | 47 | return self; 48 | } 49 | 50 | - (void)startWorkWithOption:(FFHookOption)option callBackBlock:(FFExceptionBlock)block 51 | { 52 | [self startWorkWithOption:option unrecogziedSelectorClassPrefixs:nil callBackBlock:block]; 53 | } 54 | 55 | - (void)startWorkWithOption:(FFHookOption)option unrecogziedSelectorClassPrefixs:(NSArray *)classPrefixs callBackBlock:(FFExceptionBlock)block 56 | { 57 | if (option == FFHookOptionNone) { 58 | return; 59 | } 60 | 61 | static dispatch_once_t onceToken; 62 | dispatch_once(&onceToken, ^{ 63 | 64 | if (block) { 65 | self.callBackBlock = block; 66 | } 67 | 68 | [NSObject addUnrecognizedSelectorWithClassPrefixs:classPrefixs]; 69 | 70 | if (option & FFHookOptionUnrecognizedSelector) { 71 | [NSObject startHook]; 72 | } 73 | 74 | if (option & FFHookOptionNSString) { 75 | [NSString startHook]; 76 | } 77 | 78 | if (option & FFHookOptionNSArray) { 79 | [NSArray startHook]; 80 | } 81 | 82 | if (option & FFHookOptionNSDictionary) { 83 | [NSDictionary startHook]; 84 | } 85 | 86 | // if (option & FFHookOptionNSData) { 87 | // [NSData startHook]; 88 | // } 89 | 90 | if (option & FFHookOptionNSSet) { 91 | [NSSet startHook]; 92 | } 93 | 94 | if (option & FFHookOptionNSUserDefaults) { 95 | [NSUserDefaults startHook]; 96 | } 97 | 98 | if (option & FFHookOptionNSCache) { 99 | [NSCache startHook]; 100 | } 101 | 102 | if (option & FFHookOptionNSAttributedString) { 103 | [NSAttributedString startHook]; 104 | } 105 | }); 106 | } 107 | 108 | - (void)updateUnrecogziedSelectorClassPrefixs:(NSArray *)classPrefixs 109 | { 110 | [NSObject updateUnrecogziedSelectorClassPrefixs:classPrefixs]; 111 | } 112 | 113 | #pragma mark - FFExceptionDelegate 114 | - (void)ff_captureExceptionWithErrorDic:(NSDictionary *)errorDic 115 | { 116 | if (self.callBackBlock) { 117 | self.callBackBlock(errorDic); 118 | } 119 | } 120 | 121 | 122 | @end 123 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/NSAttributedString+FFExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSAttributedString+FFExtension.h 3 | // FFExtension 4 | // 5 | // Created by 胡峰 on 2018/10/13. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSAttributedString (FFExtension) 12 | 13 | + (void)startHook; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/NSAttributedString+FFExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSAttributedString+FFExtension.m 3 | // FFExtension 4 | // 5 | // Created by 胡峰 on 2018/10/13. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import "NSAttributedString+FFExtension.h" 10 | #import "NSObject+methodSwizzle.h" 11 | #import "FFExceptionProxy.h" 12 | #import 13 | 14 | @implementation NSAttributedString (FFExtension) 15 | 16 | /* 17 | NSConcreteAttributedString, 18 | NSMutableAttributedString, 19 | "_NSCachedAttributedString", 20 | NSCFAttributedString, 21 | NSConcreteMutableAttributedString, 22 | NSConcreteNotifyingMutableAttributedString, 23 | "__NSCFAttributedString" 24 | */ 25 | 26 | + (void)startHook 27 | { 28 | Class originClass = [NSAttributedString class]; 29 | [self ff_classSwizzleWithClass:originClass originSelector:@selector(attributedStringWithAttachment:) swizzleSelector:@selector(ff_attributedStringWithAttachment:)]; 30 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(initWithString:) swizzleSelector:@selector(super_initWithString:)]; 31 | // [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(attribute:atIndex:longestEffectiveRange:inRange:) swizzleSelector:@selector(ff_attribute:atIndex:longestEffectiveRange:inRange:)]; 32 | 33 | 34 | Class concreteClass = NSClassFromString(@"NSConcreteAttributedString"); 35 | [self ff_instancenSwizzleWithClass:concreteClass originSelector:@selector(initWithString:) swizzleSelector:@selector(ff_initWithString:)]; 36 | // [self ff_instancenSwizzleWithClass:concreteClass originSelector:@selector(attributesAtIndex:effectiveRange:) swizzleSelector:@selector(ff_attributesAtIndex:effectiveRange:)]; 37 | // [self ff_instancenSwizzleWithClass:concreteClass originSelector:@selector(attribute:atIndex:effectiveRange:) swizzleSelector:@selector(ff_attribute:atIndex:effectiveRange:)]; 38 | [self ff_instancenSwizzleWithClass:concreteClass originSelector:@selector(attributedSubstringFromRange:) swizzleSelector:@selector(ff_attributedSubstringFromRange:)]; 39 | 40 | 41 | Class mutableClass = NSClassFromString(@"NSConcreteMutableAttributedString"); 42 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(initWithString:) swizzleSelector:@selector(ff_initWithString:)]; 43 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(initWithString:attributes:) swizzleSelector:@selector(ff_initWithString:attributes:)]; 44 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(replaceCharactersInRange:withString:) swizzleSelector:@selector(ff_replaceCharactersInRange:withString:)]; 45 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(setAttributes:range:) swizzleSelector:@selector(ff_setAttributes:range:)]; 46 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(addAttribute:value:range:) swizzleSelector:@selector(ff_addAttribute:value:range:)]; 47 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(addAttributes:range:) swizzleSelector:@selector(ff_addAttributes:range:)]; 48 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(removeAttribute:range:) swizzleSelector:@selector(ff_removeAttribute:range:)]; 49 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(setAttributedString:) swizzleSelector:@selector(ff_setAttributedString:)]; 50 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(replaceCharactersInRange:withAttributedString:) swizzleSelector:@selector(ff_replaceCharactersInRange:withAttributedString:)]; 51 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(deleteCharactersInRange:) swizzleSelector:@selector(ff_deleteCharactersInRange:)]; 52 | } 53 | 54 | + (NSAttributedString *)ff_attributedStringWithAttachment:(NSTextAttachment *)attachment 55 | { 56 | if (attachment) { 57 | return [self ff_attributedStringWithAttachment:attachment]; 58 | } 59 | 60 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], attachment can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; 61 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 62 | return nil; 63 | } 64 | 65 | - (instancetype)super_initWithString:(NSString *)str 66 | { 67 | if (str) { 68 | return [self super_initWithString:str]; 69 | } 70 | 71 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], str can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; 72 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 73 | return nil; 74 | } 75 | 76 | - (instancetype)ff_initWithString:(NSString *)str 77 | { 78 | if (str) { 79 | return [self ff_initWithString:str]; 80 | } 81 | 82 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], str can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; 83 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 84 | return nil; 85 | } 86 | 87 | - (instancetype)ff_initWithString:(NSString *)str attributes:(NSDictionary *)attrs 88 | { 89 | if (str) { 90 | return [self ff_initWithString:str attributes:attrs]; 91 | } 92 | 93 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], str can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; 94 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 95 | return nil; 96 | } 97 | 98 | /* 99 | - (NSDictionary *)ff_attributesAtIndex:(NSUInteger)location effectiveRange:(nullable NSRangePointer)range 100 | { 101 | if (location < self.length && range && (*range).location + (*range).length <= self.length) { 102 | return [self ff_attributesAtIndex:location effectiveRange:range]; 103 | } 104 | 105 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], location %lu, range %@, self.length %lu", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (long)location, NSStringFromRange(*range), (long)self.length]; 106 | NSLog(@"%@", msg); 107 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 108 | return nil; 109 | } 110 | 111 | - (nullable id)ff_attribute:(NSAttributedStringKey)attrName atIndex:(NSUInteger)location effectiveRange:(nullable NSRangePointer)range 112 | { 113 | if (attrName && location < self.length && (*range).location + (*range).length <= self.length) { 114 | return [self ff_attribute:attrName atIndex:location effectiveRange:range]; 115 | } 116 | 117 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], attrName = %@, index %lu, range %@, self.length %lu", NSStringFromClass([self class]), NSStringFromSelector(_cmd), attrName, (long)location, NSStringFromRange(*range), (long)self.length]; 118 | NSLog(@"%@", msg); 119 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 120 | return nil; 121 | } 122 | 123 | ///< attention:这个函数如果不hook直接调用会让Xcode10无法继续执行 124 | - (nullable id)ff_attribute:(NSAttributedStringKey)attrName atIndex:(NSUInteger)location longestEffectiveRange:(nullable NSRangePointer)range inRange:(NSRange)rangeLimit 125 | { 126 | if (attrName && range && (*range).location + (*range).length <= self.length) { 127 | return [self ff_attribute:attrName atIndex:location longestEffectiveRange:range inRange:rangeLimit]; 128 | } 129 | 130 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], attrName = %@, index %lu, range %@, self.length %lu", NSStringFromClass([self class]), NSStringFromSelector(_cmd), attrName, (long)location, NSStringFromRange(*range), (long)self.length]; 131 | NSLog(@"%@", msg); 132 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 133 | return nil; 134 | } 135 | 136 | */ 137 | 138 | - (NSAttributedString *)ff_attributedSubstringFromRange:(NSRange)range 139 | { 140 | if (range.location + range.length <= self.length) { 141 | return [self ff_attributedSubstringFromRange:range]; 142 | } 143 | 144 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@, self.length %lu", NSStringFromClass([self class]), NSStringFromSelector(_cmd), NSStringFromRange(range), (long)self.length]; 145 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 146 | return nil; 147 | } 148 | 149 | - (BOOL)ff_isEqualToAttributedString:(NSAttributedString *)other 150 | { 151 | if (other) { 152 | return [self ff_isEqualToAttributedString:other]; 153 | } 154 | 155 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], attributedString can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; 156 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 157 | return NO; 158 | } 159 | 160 | ///< mutable 161 | - (void)ff_replaceCharactersInRange:(NSRange)range withString:(NSString *)str 162 | { 163 | if (str && range.location + range.length <= self.length) { 164 | return [self ff_replaceCharactersInRange:range withString:str]; 165 | } 166 | 167 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@, self.length %lu, str is %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), NSStringFromRange(range), (long)self.length, str]; 168 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 169 | } 170 | 171 | - (void)ff_setAttributes:(nullable NSDictionary *)attrs range:(NSRange)range 172 | { 173 | if (range.location + range.length <= self.length) { 174 | return [self ff_setAttributes:attrs range:range]; 175 | } 176 | 177 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@, self.length %lu", NSStringFromClass([self class]), NSStringFromSelector(_cmd), NSStringFromRange(range), (long)self.length]; 178 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 179 | } 180 | 181 | - (void)ff_addAttribute:(NSAttributedStringKey)name value:(id)value range:(NSRange)range 182 | { 183 | if (name && value && range.location + range.length <= self.length) { 184 | return [self ff_addAttribute:name value:value range:range]; 185 | } 186 | 187 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], name %@, value %@ range %@, self.length %lu", NSStringFromClass([self class]), NSStringFromSelector(_cmd), NSStringFromRange(range), name, value, (long)self.length]; 188 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 189 | } 190 | 191 | - (void)ff_addAttributes:(NSDictionary *)attrs range:(NSRange)range 192 | { 193 | if (attrs && range.location + range.length <= self.length) { 194 | return [self ff_addAttributes:attrs range:range]; 195 | } 196 | 197 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], attrs is %@, range %@, self.length %lu", NSStringFromClass([self class]), NSStringFromSelector(_cmd), attrs, NSStringFromRange(range), (long)self.length]; 198 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 199 | } 200 | 201 | - (void)ff_removeAttribute:(NSAttributedStringKey)name range:(NSRange)range 202 | { 203 | if (name && range.location + range.length <= self.length) { 204 | return [self ff_removeAttribute:name range:range]; 205 | } 206 | 207 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], name is %@, range %@, self.length %lu", NSStringFromClass([self class]), NSStringFromSelector(_cmd), name, NSStringFromRange(range), (long)self.length]; 208 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 209 | } 210 | 211 | - (void)ff_setAttributedString:(NSAttributedString *)attrString 212 | { 213 | if (attrString && [attrString isKindOfClass:NSAttributedString.class]) { 214 | return [self ff_setAttributedString:attrString]; 215 | } 216 | 217 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], attrString is %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), attrString]; 218 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 219 | } 220 | 221 | - (void)ff_replaceCharactersInRange:(NSRange)range withAttributedString:(NSAttributedString *)attrString 222 | { 223 | if (attrString && [attrString isKindOfClass:NSAttributedString.class] && range.location + range.length <= self.length) { 224 | return [self ff_replaceCharactersInRange:range withAttributedString:attrString]; 225 | } 226 | 227 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], attrString is %@, range %@, self.length %lu", NSStringFromClass([self class]), NSStringFromSelector(_cmd), attrString, NSStringFromRange(range), (long)self.length]; 228 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 229 | } 230 | 231 | - (void)ff_deleteCharactersInRange:(NSRange)range 232 | { 233 | if (range.location + range.length <= self.length) { 234 | return [self ff_deleteCharactersInRange:range]; 235 | } 236 | 237 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@, self.length %lu,", NSStringFromClass([self class]), NSStringFromSelector(_cmd), NSStringFromRange(range), (long)self.length]; 238 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 239 | } 240 | 241 | @end 242 | 243 | 244 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/NSCache+FFExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSCache+FFExtension.h 3 | // FFExtension 4 | // 5 | // Created by hufeng on 25/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSCache (FFExtension) 12 | 13 | + (void)startHook; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/NSCache+FFExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSCache+FFExtension.m 3 | // FFExtension 4 | // 5 | // Created by hufeng on 25/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import "NSCache+FFExtension.h" 10 | #import "NSObject+methodSwizzle.h" 11 | #import "FFExceptionProxy.h" 12 | 13 | @implementation NSCache (FFExtension) 14 | 15 | + (void)startHook 16 | { 17 | [self ff_instancenSwizzleWithClass:[NSCache class] originSelector:@selector(setObject:forKey:) swizzleSelector:@selector(ff_setObject:forKey:)]; 18 | [self ff_instancenSwizzleWithClass:[NSCache class] originSelector:@selector(setObject:forKey:cost:) swizzleSelector:@selector(ff_setObject:forKey:cost:)]; 19 | } 20 | 21 | - (void)ff_setObject:(id)obj forKey:(id)key 22 | { 23 | if (obj && key) { 24 | return [self ff_setObject:obj forKey:key]; 25 | } 26 | 27 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], key %@ or object %@ can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd), key, obj]; 28 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 29 | } 30 | 31 | - (void)ff_setObject:(id)obj forKey:(id)key cost:(NSUInteger)g 32 | { 33 | if (obj && key) { 34 | return [self ff_setObject:obj forKey:key cost:g]; 35 | } 36 | 37 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], key %@ or object %@ can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd), key, obj]; 38 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/NSData+FFExtension.h: -------------------------------------------------------------------------------- 1 | // NSArray and NSDictionary(and mutable) writeToURL:error method calls this 2 | // NSData+FFExtension.h 3 | // FFExtension 4 | // 5 | // Created by 胡峰 on 2018/9/24. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSData (FFExtension) 12 | 13 | + (void)startHook; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/NSData+FFExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+FFExtension.m 3 | // FFExtension 4 | // 5 | // Created by 胡峰 on 2018/9/24. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import "NSData+FFExtension.h" 10 | #import "NSObject+methodSwizzle.h" 11 | #import "FFExceptionProxy.h" 12 | 13 | @implementation NSData (FFExtension) 14 | 15 | + (void)startHook 16 | { 17 | Class originClass = NSClassFromString(@"NSData"); 18 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(getBytes:range:) swizzleSelector:@selector(super_getBytes:range:)]; 19 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(getBytes:length:) swizzleSelector:@selector(super_getBytes:length:)]; 20 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(subdataWithRange:) swizzleSelector:@selector(super_subdataWithRange:)]; 21 | 22 | Class dataClass = NSClassFromString(@"__NSCFData"); 23 | [self ff_instancenSwizzleWithClass:dataClass originSelector:@selector(writeToURL:options:error:) swizzleSelector:@selector(ff_writeToURL:options:error:)]; 24 | 25 | Class zeroClass = NSClassFromString(@"_NSZeroData"); 26 | [self ff_instancenSwizzleWithClass:zeroClass originSelector:@selector(subdataWithRange:) swizzleSelector:@selector(ff_subdataWithRange:)]; 27 | [self ff_instancenSwizzleWithClass:zeroClass originSelector:@selector(rangeOfData:options:range:) swizzleSelector:@selector(ff_rangeOfData:options:range:)]; 28 | [self ff_instancenSwizzleWithClass:zeroClass originSelector:@selector(getBytes:range:) swizzleSelector:@selector(ff_getBytes:range:)]; 29 | 30 | Class inlineClass = NSClassFromString(@"_NSInlineData"); 31 | [self ff_instancenSwizzleWithClass:inlineClass originSelector:@selector(subdataWithRange:) swizzleSelector:@selector(ff_subdataWithRange:)]; 32 | [self ff_instancenSwizzleWithClass:inlineClass originSelector:@selector(rangeOfData:options:range:) swizzleSelector:@selector(ff_rangeOfData:options:range:)]; 33 | [self ff_instancenSwizzleWithClass:inlineClass originSelector:@selector(getBytes:range:) swizzleSelector:@selector(ff_getBytes:range:)]; 34 | [self ff_instancenSwizzleWithClass:inlineClass originSelector:@selector(getBytes:length:) swizzleSelector:@selector(ff_getBytes:length:)]; 35 | 36 | Class mutableClass = NSClassFromString(@"NSConcreteMutableData"); 37 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(writeToURL:options:error:) swizzleSelector:@selector(mutable_writeToURL:options:error:)]; 38 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(subdataWithRange:) swizzleSelector:@selector(ff_subdataWithRange:)]; 39 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(rangeOfData:options:range:) swizzleSelector:@selector(ff_rangeOfData:options:range:)]; 40 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(getBytes:range:) swizzleSelector:@selector(ff_getBytes:range:)]; 41 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(appendBytes:length:) swizzleSelector:@selector(ff_appendBytes:length:)]; 42 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(replaceBytesInRange:withBytes:) swizzleSelector:@selector(ff_replaceBytesInRange:withBytes:)]; 43 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(replaceBytesInRange:withBytes:length:) swizzleSelector:@selector(ff_replaceBytesInRange:withBytes:length:)]; 44 | 45 | Class placeClass = NSClassFromString(@"_NSPlaceholderData"); 46 | [self ff_instancenSwizzleWithClass:placeClass originSelector:@selector(initWithBase64EncodedString:options:) swizzleSelector:@selector(ff_initWithBase64EncodedString:options:)]; 47 | [self ff_instancenSwizzleWithClass:placeClass originSelector:@selector(initWithBase64EncodedData:options:) swizzleSelector:@selector(ff_initWithBase64EncodedData:options:)]; 48 | 49 | Class concreteClass = NSClassFromString(@"NSConcreteData"); 50 | [self ff_instancenSwizzleWithClass:concreteClass originSelector:@selector(getBytes:range:) swizzleSelector:@selector(ff_getBytes:range:)]; 51 | [self ff_instancenSwizzleWithClass:concreteClass originSelector:@selector(getBytes:length:) swizzleSelector:@selector(ff_getBytes:length:)]; 52 | } 53 | 54 | - (BOOL)ff_writeToURL:(NSURL *)url options:(NSDataWritingOptions)writeOptionsMask error:(NSError * _Nullable __autoreleasing *)errorPtr 55 | { 56 | if (!url) { 57 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], url can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; 58 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 59 | return NO; 60 | } 61 | 62 | return [self ff_writeToURL:url options:writeOptionsMask error:errorPtr]; 63 | } 64 | 65 | - (BOOL)mutable_writeToURL:(NSURL *)url options:(NSDataWritingOptions)writeOptionsMask error:(NSError * _Nullable __autoreleasing *)errorPtr 66 | { 67 | if (!url) { 68 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], url can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; 69 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 70 | return NO; 71 | } 72 | 73 | return [self mutable_writeToURL:url options:writeOptionsMask error:errorPtr]; 74 | } 75 | 76 | - (NSData *)super_subdataWithRange:(NSRange)range 77 | { 78 | if (range.location + range.length <= self.length) { 79 | return [self super_subdataWithRange:range]; 80 | } 81 | 82 | long tempLength = self.length > 0 ? self.length - 1 : self.length; 83 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@ is out of bounds 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), NSStringFromRange(range), (long)tempLength]; 84 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 85 | 86 | return nil; 87 | } 88 | 89 | - (NSData *)ff_subdataWithRange:(NSRange)range 90 | { 91 | if (range.location + range.length <= self.length) { 92 | return [self ff_subdataWithRange:range]; 93 | } 94 | 95 | long tempLength = self.length > 0 ? self.length - 1 : self.length; 96 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@ is out of bounds 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), NSStringFromRange(range), (long)tempLength]; 97 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 98 | 99 | return nil; 100 | } 101 | 102 | - (NSRange)ff_rangeOfData:(NSData *)dataToFind options:(NSDataSearchOptions)mask range:(NSRange)searchRange 103 | { 104 | if (searchRange.location + searchRange.length <= self.length) { 105 | return [self ff_rangeOfData:dataToFind options:mask range:searchRange]; 106 | } 107 | 108 | long tempLength = self.length > 0 ? self.length - 1 : self.length; 109 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@ is out of bounds 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), NSStringFromRange(searchRange), (long)tempLength]; 110 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 111 | 112 | return NSMakeRange(0, 0); 113 | } 114 | 115 | - (void)super_getBytes:(void *)buffer length:(NSUInteger)length 116 | { 117 | ///< 这里如果不拦截length的越界,系统会报BAD_ACCESS 118 | if (buffer && length <= self.length) { 119 | return [self super_getBytes:buffer length:length]; 120 | } 121 | 122 | long tempLength = self.length > 0 ? self.length - 1 : self.length; 123 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], buffer is %p, length %ld is out of bounds 0...%lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), buffer, (long)length, (long)tempLength]; 124 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 125 | } 126 | 127 | - (void)super_getBytes:(void *)buffer range:(NSRange)range 128 | { 129 | if (buffer && range.location + range.length <= self.length) { 130 | return [self super_getBytes:buffer range:range]; 131 | } 132 | 133 | long tempLength = self.length > 0 ? self.length - 1 : self.length; 134 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], buffer is %p, range %@ is out of bounds 0...%lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), buffer, NSStringFromRange(range), tempLength]; 135 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 136 | } 137 | 138 | ///< 这两个函数不用处理越界,系统可能内部兼容了 139 | - (void)ff_getBytes:(void *)buffer length:(NSUInteger)length 140 | { 141 | if (buffer) { 142 | return [self ff_getBytes:buffer length:length]; 143 | } 144 | 145 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], buffer can not be NULL", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 146 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 147 | } 148 | 149 | - (void)ff_getBytes:(void *)buffer range:(NSRange)range 150 | { 151 | if (buffer) { 152 | return [self ff_getBytes:buffer range:range]; ///< 某些时候,这个函数会调用上述super_getBytes:range: 153 | } 154 | 155 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], buffer can not be NULL", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 156 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 157 | } 158 | 159 | - (nullable instancetype)ff_initWithBase64EncodedString:(NSString *)base64String options:(NSDataBase64DecodingOptions)options 160 | { 161 | if (base64String) { 162 | return [self ff_initWithBase64EncodedString:base64String options:options]; 163 | } 164 | 165 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], base64String can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 166 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 167 | return nil; 168 | } 169 | 170 | - (nullable instancetype)ff_initWithBase64EncodedData:(NSData *)base64Data options:(NSDataBase64DecodingOptions)options 171 | { 172 | if (base64Data) { 173 | return [self ff_initWithBase64EncodedData:base64Data options:options]; 174 | } 175 | 176 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], base64Data can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 177 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 178 | return nil; 179 | } 180 | 181 | - (void)ff_appendBytes:(const void *)bytes length:(NSUInteger)length 182 | { 183 | if (bytes || length == 0) { 184 | return [self ff_appendBytes:bytes length:length]; 185 | } 186 | 187 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], bytes can not be nil, length = %lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), (long)length]; 188 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 189 | } 190 | 191 | - (void)ff_replaceBytesInRange:(NSRange)range withBytes:(const void *)bytes 192 | { 193 | if (bytes && range.location <= self.length) { ///< 这个函数不需要对长度加保护,加了反而误事 194 | return [self ff_replaceBytesInRange:range withBytes:bytes]; 195 | } 196 | 197 | long tempLength = self.length > 0 ? self.length - 1 : self.length; 198 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], bytes is %p, range %@ is out of bounds 0...%lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), bytes, NSStringFromRange(range), tempLength]; 199 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 200 | } 201 | 202 | - (void)ff_replaceBytesInRange:(NSRange)range withBytes:(nullable const void *)replacementBytes length:(NSUInteger)replacementLength 203 | { 204 | if (range.location + range.length <= self.length) { 205 | return [self ff_replaceBytesInRange:range withBytes:replacementBytes length:replacementLength]; 206 | } 207 | 208 | long tempLength = self.length > 0 ? self.length - 1 : self.length; 209 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], bytes is %p, range %@ is out of bounds 0...%lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), replacementBytes, NSStringFromRange(range), tempLength]; 210 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 211 | } 212 | 213 | @end 214 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/NSDictionary+FFExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+FFExtension.h 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSDictionary (FFExtension) 12 | 13 | + (void)startHook; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/NSDictionary+FFExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+FFExtension.m 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import "NSDictionary+FFExtension.h" 10 | #import "NSObject+methodSwizzle.h" 11 | #import "FFExceptionProxy.h" 12 | 13 | @implementation NSDictionary (FFExtension) 14 | 15 | + (void)startHook 16 | { 17 | Class placeHolderClass = NSClassFromString(@"__NSPlaceholderDictionary"); 18 | [self ff_instancenSwizzleWithClass:placeHolderClass originSelector:@selector(initWithObjects:forKeys:count:) swizzleSelector:@selector(ff_initWithObjects:forKeys:count:)]; 19 | 20 | Class originClass = NSClassFromString(@"NSDictionary"); 21 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(writeToURL:error:) swizzleSelector:@selector(ff_writeToURL:error:)]; 22 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(initWithObjects:forKeys:) swizzleSelector:@selector(ff_initWithObjects:forKeys:)]; 23 | [self ff_classSwizzleWithClass:originClass originSelector:@selector(sharedKeySetForKeys:) swizzleSelector:@selector(ff_sharedKeySetForKeys:)]; 24 | 25 | Class mutableClass = NSClassFromString(@"NSMutableDictionary"); 26 | [self ff_classSwizzleWithClass:mutableClass originSelector:@selector(sharedKeySetForKeys:) swizzleSelector:@selector(mutable_sharedKeySetForKeys:)]; 27 | [self ff_classSwizzleWithClass:mutableClass originSelector:@selector(dictionaryWithSharedKeySet:) swizzleSelector:@selector(ff_dictionaryWithSharedKeySet:)]; 28 | 29 | Class classM = NSClassFromString(@"__NSDictionaryM"); 30 | [self ff_instancenSwizzleWithClass:classM originSelector:@selector(removeObjectForKey:) swizzleSelector:@selector(ff_removeObjectForKey:)]; 31 | [self ff_instancenSwizzleWithClass:classM originSelector:@selector(setObject:forKey:) swizzleSelector:@selector(ff_setObject:forKey:)]; 32 | [self ff_instancenSwizzleWithClass:classM originSelector:@selector(setObject:forKeyedSubscript:) swizzleSelector:@selector(ff_setObject:forKeyedSubscript:)]; 33 | 34 | } 35 | 36 | - (instancetype)ff_initWithObjects:(id _Nonnull const [])objects forKeys:(id _Nonnull const [])keys count:(NSUInteger)cnt 37 | { 38 | NSUInteger realCount = 0; 39 | id realObjects[cnt]; 40 | id realKeys[cnt]; 41 | 42 | BOOL capture = NO; 43 | for (NSUInteger i = 0; i < cnt; i++) { 44 | if (keys && objects && keys[i] && objects[i]) { 45 | realObjects[realCount] = objects[i]; 46 | realKeys[realCount] = keys[i]; 47 | realCount++; 48 | } else { 49 | if (!capture) { 50 | capture = YES; 51 | NSUInteger count = cnt > 0 ? (cnt -1) : cnt; 52 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], the %lu keys %p or objects %p is nil in 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), (long)i, keys, objects, (long)count]; 53 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 54 | } 55 | } 56 | } 57 | 58 | return [self ff_initWithObjects:realObjects forKeys:realKeys count:realCount]; 59 | } 60 | 61 | - (instancetype)ff_initWithObjects:(NSArray *)objects forKeys:(NSArray> *)keys 62 | { 63 | if (objects.count == keys.count) { 64 | return [self ff_initWithObjects:objects forKeys:keys]; 65 | } 66 | 67 | 68 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], objects count %lu must equal keys count %lu", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (long)objects.count, (long)keys.count]; 69 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 70 | return nil; 71 | } 72 | 73 | - (BOOL)ff_writeToURL:(NSURL *)url error:(NSError * _Nullable __autoreleasing *)error 74 | { 75 | if (url) { 76 | return [self ff_writeToURL:url error:error]; 77 | } 78 | 79 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], url can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; 80 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 81 | 82 | return NO; 83 | } 84 | 85 | + (id)ff_sharedKeySetForKeys:(NSArray> *)keys 86 | { 87 | if (keys) { 88 | return [self ff_sharedKeySetForKeys:keys]; 89 | } 90 | 91 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], keys can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; 92 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 93 | 94 | return nil; 95 | } 96 | 97 | + (id)mutable_sharedKeySetForKeys:(NSArray> *)keys 98 | { 99 | if (!keys) { 100 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], keys can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; 101 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 102 | return nil; 103 | } 104 | 105 | return [self mutable_sharedKeySetForKeys:keys]; 106 | } 107 | 108 | - (void)ff_removeObjectForKey:(id)aKey 109 | { 110 | if (aKey) { 111 | return [self ff_removeObjectForKey:aKey]; 112 | } 113 | 114 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], key can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; 115 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 116 | } 117 | 118 | - (void)ff_setObject:(id)anObject forKey:(id)aKey 119 | { 120 | if (anObject && aKey) { 121 | return [self ff_setObject:anObject forKey:aKey]; 122 | } 123 | 124 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], key %@ or object %@ can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aKey, anObject]; 125 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 126 | } 127 | 128 | - (void)ff_setObject:(nullable id)obj forKeyedSubscript:(id )key 129 | { 130 | if (key) { // if obj be nil, it will call removeObjectForKey: 131 | return [self ff_setObject:obj forKeyedSubscript:key]; 132 | } 133 | 134 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], key %@ or object %@ can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd), key, obj]; 135 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 136 | } 137 | 138 | + (NSMutableDictionary *)ff_dictionaryWithSharedKeySet:(id)keyset 139 | { 140 | if (keyset) { 141 | return [self ff_dictionaryWithSharedKeySet:keyset]; 142 | } 143 | 144 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], keySet can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; 145 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 146 | return nil; 147 | } 148 | 149 | @end 150 | 151 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/NSObject+FFExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+FFExtension.h 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSObject (FFExtension) 12 | 13 | + (void)startHook; 14 | 15 | + (void)addUnrecognizedSelectorWithClassPrefixs:(NSArray *)classPrefixs; 16 | 17 | + (void)updateUnrecogziedSelectorClassPrefixs:(NSArray *)classPrefixs; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/NSObject+FFExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+FFExtension.m 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import "NSObject+FFExtension.h" 10 | #import "NSObject+methodSwizzle.h" 11 | #import 12 | #import "pthread.h" 13 | #import "FFExceptionProxy.h" 14 | 15 | static NSMutableArray *prefixs; 16 | static pthread_mutex_t mutex; 17 | 18 | @interface ff_target : NSObject 19 | @end 20 | 21 | @implementation ff_target 22 | 23 | + (BOOL)resolveInstanceMethod:(SEL)sel 24 | { 25 | class_addMethod([self class], 26 | sel, 27 | (IMP)testAddMethod, 28 | "v@:"); 29 | return YES; 30 | } 31 | 32 | void testAddMethod(id self, SEL _cmd) {} 33 | 34 | @end 35 | 36 | @implementation NSObject (FFExtension) 37 | 38 | + (void)startHook 39 | { 40 | [NSObject ff_instancenSwizzleWithClass:[NSObject class] 41 | originSelector:@selector(forwardingTargetForSelector:) 42 | swizzleSelector:@selector(ff_forwardingTargetForSelector:)]; 43 | } 44 | 45 | + (void)addUnrecognizedSelectorWithClassPrefixs:(NSArray *)classPrefixs 46 | { 47 | static dispatch_once_t onceToken; 48 | dispatch_once(&onceToken, ^{ 49 | 50 | pthread_mutex_init(&mutex, NULL); 51 | prefixs = [NSMutableArray array]; 52 | 53 | if (classPrefixs && [classPrefixs isKindOfClass:NSArray.class]) { 54 | NSSet *set = [NSSet setWithArray:classPrefixs]; 55 | pthread_mutex_lock(&mutex); 56 | [prefixs addObjectsFromArray:set.allObjects]; 57 | pthread_mutex_unlock(&mutex); 58 | } 59 | }); 60 | } 61 | 62 | + (void)updateUnrecogziedSelectorClassPrefixs:(NSArray *)classPrefixs 63 | { 64 | if (classPrefixs && [classPrefixs isKindOfClass:NSArray.class]) { 65 | NSMutableSet *set = [NSMutableSet setWithArray:classPrefixs]; 66 | pthread_mutex_lock(&mutex); 67 | [set addObjectsFromArray:prefixs]; 68 | prefixs = [NSMutableArray arrayWithArray:set.allObjects]; 69 | pthread_mutex_unlock(&mutex); 70 | } 71 | } 72 | 73 | 74 | - (id)ff_forwardingTargetForSelector:(SEL)aSelector 75 | { 76 | NSString *className = NSStringFromClass([self class]); 77 | 78 | pthread_mutex_lock(&mutex); 79 | for (NSString *prefix in prefixs) { 80 | if ([className hasPrefix:prefix]) { 81 | pthread_mutex_unlock(&mutex); 82 | NSString *msg = [NSString stringWithFormat:@"capture unrecognized selector sent to instance, -[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(aSelector)]; 83 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 84 | return [ff_target new]; 85 | } 86 | } 87 | 88 | pthread_mutex_unlock(&mutex); 89 | return [self ff_forwardingTargetForSelector:aSelector]; 90 | } 91 | 92 | @end 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/NSSet+FFExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSSet+FFExtension.h 3 | // FFExtension 4 | // 5 | // Created by hufeng on 25/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSSet (FFExtension) 12 | 13 | + (void)startHook; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/NSSet+FFExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSSet+FFExtension.m 3 | // FFExtension 4 | // 5 | // Created by hufeng on 25/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import "NSSet+FFExtension.h" 10 | #import "NSObject+methodSwizzle.h" 11 | #import "FFExceptionProxy.h" 12 | 13 | @implementation NSSet (FFExtension) 14 | 15 | + (void)startHook 16 | { 17 | Class originClass = NSClassFromString(@"NSSet"); 18 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(setByAddingObject:) swizzleSelector:@selector(ff_setByAddingObject:)]; 19 | 20 | Class placeClass = NSClassFromString(@"__NSPlaceholderSet"); 21 | [self ff_instancenSwizzleWithClass:placeClass originSelector:@selector(initWithObjects:count:) swizzleSelector:@selector(ff_initWithObjects:count:)]; 22 | } 23 | 24 | - (instancetype)ff_initWithObjects:(const id _Nonnull [_Nullable])objects count:(NSUInteger)cnt 25 | { 26 | NSUInteger realCount = 0; 27 | id _Nonnull __unsafe_unretained realObjects[cnt]; 28 | 29 | BOOL capture = NO; 30 | for (NSUInteger i = 0; i < cnt; i++) { 31 | if (objects && objects[i]) { 32 | realObjects[realCount] = objects[i]; 33 | realCount++; 34 | } else { 35 | 36 | if (!capture) { 37 | capture = YES; 38 | NSUInteger count = cnt > 0 ? (cnt -1) : cnt; 39 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], the %lu object or objects %p is nil in 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), (long)i, objects, (long)count]; 40 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 41 | } 42 | } 43 | } 44 | 45 | return [self ff_initWithObjects:realObjects count:realCount]; 46 | } 47 | 48 | - (NSSet *)ff_setByAddingObject:(id)anObject 49 | { 50 | if (anObject) { 51 | return [self ff_setByAddingObject:anObject]; 52 | } 53 | 54 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], object can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 55 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 56 | 57 | return nil; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/NSString+FFExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+FFExtension.h 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (FFExtension) 12 | 13 | + (void)startHook; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/NSString+FFExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+FFExtension.m 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import "NSString+FFExtension.h" 10 | #import "NSObject+methodSwizzle.h" 11 | #import 12 | #import "FFExceptionProxy.h" 13 | 14 | 15 | @implementation NSString (FFExtension) 16 | 17 | + (void)startHook 18 | { 19 | Class strClass = NSClassFromString(@"NSString"); 20 | [self ff_classSwizzleWithClass:strClass originSelector:@selector(stringWithUTF8String:) swizzleSelector:@selector(ff_stringWithUTF8String:)]; 21 | [self ff_classSwizzleWithClass:strClass originSelector:@selector(stringWithCString:encoding:) swizzleSelector:@selector(ff_stringWithCString:encoding:)]; 22 | 23 | Class placeClass = NSClassFromString(@"NSPlaceholderString"); 24 | [self ff_instancenSwizzleWithClass:placeClass originSelector:@selector(initWithUTF8String:) swizzleSelector:@selector(ff_initWithUTF8String:)]; 25 | [self ff_instancenSwizzleWithClass:placeClass originSelector:@selector(initWithCString:encoding:) swizzleSelector:@selector(ff_initWithCString:encoding:)]; 26 | [self ff_instancenSwizzleWithClass:placeClass originSelector:@selector(initWithString:) swizzleSelector:@selector(ff_initWithString:)]; 27 | 28 | Class placeMutableClass = NSClassFromString(@"NSPlaceholderMutableString"); 29 | [self ff_instancenSwizzleWithClass:placeMutableClass originSelector:@selector(initWithCString:encoding:) swizzleSelector:@selector(ff_initWithCString:encoding:)]; 30 | [self ff_instancenSwizzleWithClass:placeMutableClass originSelector:@selector(initWithUTF8String:) swizzleSelector:@selector(ff_initWithUTF8String:)]; 31 | [self ff_instancenSwizzleWithClass:placeMutableClass originSelector:@selector(initWithString:) swizzleSelector:@selector(ff_initWithString:)]; 32 | 33 | Class originClass = NSClassFromString(@"__NSCFConstantString"); 34 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(characterAtIndex:) swizzleSelector:@selector(ff_characterAtIndex:)]; 35 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(substringFromIndex:) swizzleSelector:@selector(ff_substringFromIndex:)]; 36 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(substringToIndex:) swizzleSelector:@selector(ff_substringToIndex:)]; 37 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(substringWithRange:) swizzleSelector:@selector(ff_substringWithRange:)]; 38 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(hasPrefix:) swizzleSelector:@selector(ff_hasPrefix:)]; 39 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(hasSuffix:) swizzleSelector:@selector(ff_hasSuffix:)]; 40 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(rangeOfString:options:range:locale:) swizzleSelector:@selector(ff_rangeOfString:options:range:locale:)]; 41 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(rangeOfComposedCharacterSequenceAtIndex:) swizzleSelector:@selector(ff_rangeOfComposedCharacterSequenceAtIndex:)]; 42 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(rangeOfComposedCharacterSequencesForRange:) swizzleSelector:@selector(ff_rangeOfComposedCharacterSequencesForRange:)]; 43 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(stringByAppendingString:) swizzleSelector:@selector(ff_stringByAppendingString:)]; 44 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(stringByReplacingOccurrencesOfString:withString:) swizzleSelector:@selector(ff_stringByReplacingOccurrencesOfString:withString:)]; 45 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(stringByReplacingCharactersInRange:withString:) swizzleSelector:@selector(ff_stringByReplacingCharactersInRange:withString:)]; 46 | 47 | 48 | Class mutableClass = NSClassFromString(@"__NSCFString"); 49 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(characterAtIndex:) swizzleSelector:@selector(ff_characterAtIndex:)]; 50 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(substringFromIndex:) swizzleSelector:@selector(ff_substringFromIndex:)]; 51 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(substringToIndex:) swizzleSelector:@selector(ff_substringToIndex:)]; 52 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(substringWithRange:) swizzleSelector:@selector(ff_substringWithRange:)]; 53 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(hasPrefix:) swizzleSelector:@selector(ff_hasPrefix:)]; 54 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(hasSuffix:) swizzleSelector:@selector(ff_hasSuffix:)]; 55 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(rangeOfString:options:range:locale:) swizzleSelector:@selector(ff_rangeOfString:options:range:locale:)]; 56 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(rangeOfComposedCharacterSequenceAtIndex:) swizzleSelector:@selector(ff_rangeOfComposedCharacterSequenceAtIndex:)]; 57 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(rangeOfComposedCharacterSequencesForRange:) swizzleSelector:@selector(ff_rangeOfComposedCharacterSequencesForRange:)]; 58 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(stringByAppendingString:) swizzleSelector:@selector(ff_stringByAppendingString:)]; 59 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(stringByReplacingOccurrencesOfString:withString:) swizzleSelector:@selector(ff_stringByReplacingOccurrencesOfString:withString:)]; 60 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(stringByReplacingCharactersInRange:withString:) swizzleSelector:@selector(ff_stringByReplacingCharactersInRange:withString:)]; 61 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(stringByReplacingOccurrencesOfString:withString:options:range:) swizzleSelector:@selector(ff_stringByReplacingOccurrencesOfString:withString:options:range:)]; 62 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(replaceCharactersInRange:withString:) swizzleSelector:@selector(ff_replaceCharactersInRange:withString:)]; 63 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(insertString:atIndex:) swizzleSelector:@selector(ff_insertString:atIndex:)]; 64 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(deleteCharactersInRange:) swizzleSelector:@selector(ff_deleteCharactersInRange:)]; 65 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(appendString:) swizzleSelector:@selector(ff_appendString:)]; 66 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(setString:) swizzleSelector:@selector(ff_setString:)]; 67 | 68 | 69 | Class tagClass = NSClassFromString(@"NSTaggedPointerString"); 70 | [self ff_instancenSwizzleWithClass:tagClass originSelector:@selector(characterAtIndex:) swizzleSelector:@selector(ff_characterAtIndex:)]; 71 | [self ff_instancenSwizzleWithClass:tagClass originSelector:@selector(substringWithRange:) swizzleSelector:@selector(ff_substringWithRange:)]; 72 | 73 | 74 | Class bigClass = NSClassFromString(@"NSBigMutableString"); ///< 某些系统的键盘会用到这个类 75 | [self ff_instancenSwizzleWithClass:bigClass originSelector:@selector(characterAtIndex:) swizzleSelector:@selector(ff_characterAtIndex:)]; 76 | [self ff_instancenSwizzleWithClass:bigClass originSelector:@selector(substringWithRange:) swizzleSelector:@selector(ff_substringWithRange:)]; 77 | [self ff_instancenSwizzleWithClass:bigClass originSelector:@selector(replaceCharactersInRange:withString:) swizzleSelector:@selector(ff_replaceCharactersInRange:withString:)]; 78 | // getCharacters:range: 79 | } 80 | 81 | + (instancetype)ff_stringWithUTF8String:(const char *)nullTerminatedCString 82 | { 83 | if (nullTerminatedCString) { 84 | return [self ff_stringWithUTF8String:nullTerminatedCString]; 85 | } 86 | 87 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], object can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 88 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 89 | return nil; 90 | } 91 | 92 | - (instancetype)ff_initWithUTF8String:(const char *)nullTerminatedCString 93 | { 94 | if (nullTerminatedCString) { 95 | return [self ff_initWithUTF8String:nullTerminatedCString]; 96 | } 97 | 98 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], object can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 99 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 100 | return nil; 101 | } 102 | 103 | + (instancetype)ff_stringWithCString:(const char *)cString encoding:(NSStringEncoding)enc 104 | { 105 | if (cString) { 106 | return [self ff_stringWithCString:cString encoding:enc]; 107 | } 108 | 109 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], object can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 110 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 111 | return nil; 112 | } 113 | 114 | - (instancetype)ff_initWithCString:(const char *)nullTerminatedCString encoding:(NSStringEncoding)encoding 115 | { 116 | if (nullTerminatedCString) { 117 | return [self ff_initWithCString:nullTerminatedCString encoding:encoding]; 118 | } 119 | 120 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], object can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 121 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 122 | return nil; 123 | } 124 | 125 | - (instancetype)ff_initWithString:(NSString *)aString 126 | { 127 | if (aString) { 128 | return [self ff_initWithString:aString]; 129 | } 130 | 131 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], object can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 132 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 133 | return nil; 134 | } 135 | 136 | - (unichar)ff_characterAtIndex:(NSUInteger)index 137 | { 138 | if (index < self.length) { 139 | return [self ff_characterAtIndex:index]; 140 | } 141 | 142 | long length = self.length > 0 ? self.length - 1 : self.length; 143 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], index %lu is out of bounds 0...%lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), (long)index, length]; 144 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 145 | 146 | return 0; 147 | } 148 | 149 | - (NSString *)ff_substringFromIndex:(NSUInteger)from 150 | { 151 | if (from <= self.length) { 152 | return [self ff_substringFromIndex:from]; 153 | } 154 | 155 | long length = self.length > 0 ? self.length - 1 : self.length; 156 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], index %lu is out of bounds 0...%lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), (long)from, length]; 157 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 158 | return [self ff_substringFromIndex:self.length]; 159 | } 160 | 161 | - (NSString *)ff_substringToIndex:(NSUInteger)to 162 | { 163 | if (to <= self.length) { 164 | return [self ff_substringToIndex:to]; 165 | } 166 | 167 | long length = self.length > 0 ? self.length - 1 : self.length; 168 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], index %lu is out of bounds 0...%lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), (long)to, length]; 169 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 170 | return [self ff_substringToIndex:self.length]; 171 | } 172 | 173 | - (NSString *)ff_substringWithRange:(NSRange)range 174 | { 175 | if (range.location + range.length <= self.length) { 176 | return [self ff_substringWithRange:range]; 177 | } 178 | 179 | long length = self.length > 0 ? self.length - 1 : self.length; 180 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@ is out of bounds 0...%lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), NSStringFromRange(range), length]; 181 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 182 | return nil; 183 | } 184 | 185 | - (BOOL)ff_hasPrefix:(NSString *)str 186 | { 187 | if (str) { 188 | return [self ff_hasPrefix:str]; 189 | } 190 | 191 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], str can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 192 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 193 | return NO; 194 | } 195 | 196 | - (BOOL)ff_hasSuffix:(NSString *)str 197 | { 198 | if (str) { 199 | return [self ff_hasSuffix:str]; 200 | } 201 | 202 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], str can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 203 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 204 | return NO; 205 | } 206 | 207 | - (NSRange)ff_rangeOfString:(NSString *)searchString options:(NSStringCompareOptions)mask range:(NSRange)rangeOfReceiverToSearch locale:(nullable NSLocale *)locale 208 | { 209 | if (searchString && rangeOfReceiverToSearch.location + rangeOfReceiverToSearch.length <= self.length) { 210 | return [self ff_rangeOfString:searchString options:mask range:rangeOfReceiverToSearch locale:locale]; 211 | } 212 | 213 | long length = self.length > 0 ? self.length - 1 : self.length; 214 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], str = %@, range = %@, self.length = %lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), searchString, NSStringFromRange(rangeOfReceiverToSearch), (long)length]; 215 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 216 | return NSMakeRange(0, 0); 217 | } 218 | 219 | - (NSRange)ff_rangeOfComposedCharacterSequenceAtIndex:(NSUInteger)index 220 | { 221 | if (index < self.length) { 222 | return [self ff_rangeOfComposedCharacterSequenceAtIndex:index]; 223 | } 224 | 225 | long length = self.length > 0 ? self.length - 1 : self.length; 226 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], index %lu is out of bounds 0...%lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), (long)index, length]; 227 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 228 | return NSMakeRange(0, 0); 229 | } 230 | 231 | - (NSRange)ff_rangeOfComposedCharacterSequencesForRange:(NSRange)range 232 | { 233 | if (range.location + range.length <= self.length) { 234 | return [self ff_rangeOfComposedCharacterSequencesForRange:range]; 235 | } 236 | 237 | long length = self.length > 0 ? self.length - 1 : self.length; 238 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@ is out of bounds 0...%lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), NSStringFromRange(range), length]; 239 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 240 | return NSMakeRange(0, 0); 241 | } 242 | 243 | - (NSString *)ff_stringByAppendingString:(NSString *)aString 244 | { 245 | if (aString) { 246 | return [self ff_stringByAppendingString:aString]; 247 | } 248 | 249 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], str can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 250 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 251 | return self; 252 | } 253 | 254 | - (NSString *)ff_stringByReplacingOccurrencesOfString:(NSString *)target withString:(NSString *)replacement 255 | { 256 | if (target && replacement) { 257 | return [self ff_stringByReplacingOccurrencesOfString:target withString:replacement]; 258 | } 259 | 260 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], both target str %@ and replace str %@ can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd), target, replacement]; 261 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 262 | return self; 263 | } 264 | 265 | - (NSString *)ff_stringByReplacingCharactersInRange:(NSRange)range withString:(NSString *)replacement 266 | { 267 | if (replacement && range.location + range.length <= self.length) { 268 | return [self ff_stringByReplacingCharactersInRange:range withString:replacement]; 269 | } 270 | 271 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@ and total length %lu, replace str is %@ ", NSStringFromClass([self class]), NSStringFromSelector(_cmd),NSStringFromRange(range), (long)self.length, replacement]; 272 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 273 | return self; 274 | } 275 | 276 | - (void)ff_replaceCharactersInRange:(NSRange)range withString:(NSString *)aString 277 | { 278 | if (aString && range.location + range.length <= self.length) { 279 | return [self ff_replaceCharactersInRange:range withString:aString]; 280 | } 281 | 282 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@ and total length %lu, str is %@ ", NSStringFromClass([self class]), NSStringFromSelector(_cmd),NSStringFromRange(range), (long)self.length, aString]; 283 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 284 | } 285 | 286 | - (void)ff_insertString:(NSString *)aString atIndex:(NSUInteger)loc 287 | { 288 | if (aString && loc <= self.length) { 289 | return [self ff_insertString:aString atIndex:loc]; 290 | } 291 | 292 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], index %lu and total length %lu, str is %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (long)loc, (long)self.length, aString]; 293 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 294 | } 295 | 296 | - (void)ff_deleteCharactersInRange:(NSRange)range 297 | { 298 | if (range.location + range.length <= self.length) { 299 | return [self ff_deleteCharactersInRange:range]; 300 | } 301 | 302 | long length = self.length > 0 ? self.length - 1 : self.length; 303 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@ is out of bounds 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), NSStringFromRange(range), length]; 304 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 305 | } 306 | 307 | - (void)ff_appendString:(NSString *)aString 308 | { 309 | if (aString) { 310 | return [self ff_appendString:aString]; 311 | } 312 | 313 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], str can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 314 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 315 | } 316 | 317 | - (void)ff_setString:(NSString *)aString 318 | { 319 | if (aString) { 320 | return [self ff_setString:aString]; 321 | } 322 | 323 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], str can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 324 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 325 | } 326 | 327 | - (NSString *)ff_stringByReplacingOccurrencesOfString:(NSString *)target withString:(NSString *)replacement options:(NSStringCompareOptions)options range:(NSRange)searchRange 328 | { 329 | if (target && replacement) { 330 | return [self ff_stringByReplacingOccurrencesOfString:target withString:replacement options:options range:searchRange]; 331 | } 332 | 333 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], target str %@ and replace str %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), target, replacement]; 334 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 335 | return self; 336 | } 337 | 338 | @end 339 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/NSUserDefaults+FFExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSUserDefaults+FFExtension.h 3 | // FFExtension 4 | // 5 | // Created by hufeng on 25/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSUserDefaults (FFExtension) 12 | 13 | + (void)startHook; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /FFExtension/Classes/Foundation/NSUserDefaults+FFExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSUserDefaults+FFExtension.m 3 | // FFExtension 4 | // 5 | // Created by hufeng on 25/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import "NSUserDefaults+FFExtension.h" 10 | #import "NSObject+methodSwizzle.h" 11 | #import "FFExceptionProxy.h" 12 | 13 | @implementation NSUserDefaults (FFExtension) 14 | 15 | + (void)startHook 16 | { 17 | Class originClass = NSClassFromString(@"NSUserDefaults"); 18 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(objectForKey:) swizzleSelector:@selector(ff_objectForKey:)]; 19 | 20 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(setObject:forKey:) swizzleSelector:@selector(ff_setObject:forKey:)]; 21 | 22 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(removeObjectForKey:) swizzleSelector:@selector(ff_removeObjectForKey:)]; 23 | 24 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(integerForKey:) swizzleSelector:@selector(ff_integerForKey:)]; 25 | 26 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(boolForKey:) swizzleSelector:@selector(ff_boolForKey:)]; 27 | 28 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(addSuiteNamed:) swizzleSelector:@selector(ff_addSuiteNamed:)]; 29 | 30 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(removeSuiteNamed:) swizzleSelector:@selector(ff_removeSuiteNamed:)]; 31 | 32 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(volatileDomainForName:) swizzleSelector:@selector(ff_volatileDomainForName:)]; 33 | 34 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(setVolatileDomain:forName:) swizzleSelector:@selector(ff_setVolatileDomain:forName:)]; 35 | 36 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(removeVolatileDomainForName:) swizzleSelector:@selector(ff_removeVolatileDomainForName:)]; 37 | 38 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(objectIsForcedForKey:inDomain:) swizzleSelector:@selector(ff_objectIsForcedForKey:inDomain:)]; 39 | } 40 | 41 | - (nullable id)ff_objectForKey:(NSString *)defaultName 42 | { 43 | if (defaultName) { 44 | return [self ff_objectForKey:defaultName]; 45 | } 46 | 47 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], key can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 48 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 49 | 50 | return nil; 51 | } 52 | 53 | - (void)ff_setObject:(nullable id)value forKey:(NSString *)defaultName 54 | { 55 | if (defaultName) { // if value be nil, it will call removeObjectForKey: 56 | return [self ff_setObject:value forKey:defaultName]; 57 | } 58 | 59 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], key %@ or object %@ can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd), defaultName, value]; 60 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 61 | } 62 | 63 | - (void)ff_removeObjectForKey:(NSString *)defaultName 64 | { 65 | if (defaultName) { 66 | return [self ff_removeObjectForKey:defaultName]; 67 | } 68 | 69 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], key can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 70 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 71 | } 72 | 73 | - (NSInteger)ff_integerForKey:(NSString *)defaultName 74 | { 75 | if (defaultName) { 76 | return [self ff_integerForKey:defaultName]; 77 | } 78 | 79 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], key can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 80 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 81 | 82 | return 0; 83 | } 84 | 85 | - (BOOL)ff_boolForKey:(NSString *)defaultName 86 | { 87 | if (defaultName) { 88 | return [self ff_boolForKey:defaultName]; 89 | } 90 | 91 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], key can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 92 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 93 | 94 | return NO; 95 | } 96 | 97 | - (void)ff_addSuiteNamed:(NSString *)suiteName 98 | { 99 | if (suiteName) { 100 | return [self ff_addSuiteNamed:suiteName]; 101 | } 102 | 103 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], suiteName can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 104 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 105 | } 106 | 107 | - (void)ff_removeSuiteNamed:(NSString *)suiteName; 108 | { 109 | if (suiteName) { 110 | return [self ff_removeSuiteNamed:suiteName]; 111 | } 112 | 113 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], suiteName can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 114 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 115 | } 116 | 117 | - (NSDictionary *)ff_volatileDomainForName:(NSString *)domainName 118 | { 119 | if (domainName) { 120 | return [self ff_volatileDomainForName:domainName]; 121 | } 122 | 123 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], domainName can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 124 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 125 | 126 | return nil; 127 | } 128 | 129 | - (void)ff_setVolatileDomain:(NSDictionary *)domain forName:(NSString *)domainName 130 | { 131 | if (domain && domainName) { 132 | return [self ff_setVolatileDomain:domain forName:domainName]; 133 | } 134 | 135 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], domain %@ or domainName %@ can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd), domain, domainName]; 136 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 137 | } 138 | 139 | - (void)ff_removeVolatileDomainForName:(NSString *)domainName 140 | { 141 | if (domainName) { 142 | return [self ff_removeVolatileDomainForName:domainName]; 143 | } 144 | 145 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], domainName can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 146 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 147 | } 148 | 149 | - (BOOL)ff_objectIsForcedForKey:(NSString *)key inDomain:(NSString *)domain 150 | { 151 | if (domain) { 152 | return [self ff_objectIsForcedForKey:key inDomain:domain]; 153 | } 154 | 155 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], domain can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 156 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 157 | 158 | return NO; 159 | } 160 | 161 | @end 162 | -------------------------------------------------------------------------------- /FFExtension/Classes/MRC/NSArray+FFExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+FFExtension.h 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSArray (FFExtension) 12 | 13 | + (void)startHook; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /FFExtension/Classes/MRC/NSArray+FFExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+FFExtension.m 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import "NSArray+FFExtension.h" 10 | #import "NSObject+methodSwizzle.h" 11 | #import "FFExceptionProxy.h" 12 | 13 | @implementation NSArray (FFExtension) 14 | 15 | ///< TODO: 想一下为什么这里的同一个函数,不能被两个类去交换,按理来说,会分别拷贝到不同的类去,是没有关系的,但是实际应用如果不分开实现会崩溃————因为不能动NSArray这个父类,只要不动父类,子类随便玩 16 | 17 | 18 | + (void)startHook 19 | { 20 | SEL getObjectsRangeSEL = @selector(ff_getObjects:range:); 21 | SEL objectsAtIndexSEL = @selector(ff_objectAtIndex:); 22 | 23 | Class arrayClass = NSClassFromString(@"NSArray"); 24 | [self ff_instancenSwizzleWithClass:arrayClass originSelector:@selector(arrayByAddingObject:) swizzleSelector:@selector(ff_arrayByAddingObject:)]; 25 | [self ff_instancenSwizzleWithClass:arrayClass originSelector:@selector(indexOfObject:inRange:) swizzleSelector:@selector(ff_indexOfObject:inRange:)]; 26 | [self ff_instancenSwizzleWithClass:arrayClass originSelector:@selector(getObjects:range:) swizzleSelector:@selector(ff_getObjectsForSuperClass:range:)]; ///< 这里要注意不能把父类的方法给替换了,否则会影响到所有子类 27 | [self ff_instancenSwizzleWithClass:arrayClass originSelector:@selector(indexOfObjectIdenticalTo:inRange:) swizzleSelector:@selector(ff_indexOfObjectIdenticalTo:inRange:)]; 28 | [self ff_instancenSwizzleWithClass:arrayClass originSelector:@selector(subarrayWithRange:) swizzleSelector:@selector(ff_subarrayWithRange:)]; 29 | [self ff_instancenSwizzleWithClass:arrayClass originSelector:@selector(objectsAtIndexes:) swizzleSelector:@selector(ff_objectsAtIndexes:)]; 30 | [self ff_instancenSwizzleWithClass:arrayClass originSelector:@selector(writeToURL:error:) swizzleSelector:@selector(ff_writeToURL:error:)]; ///< for NSData 31 | 32 | 33 | 34 | Class originClass = NSClassFromString(@"__NSArrayI"); 35 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(objectAtIndexedSubscript:) swizzleSelector:@selector(ff_objectAtIndexedSubscript:)]; 36 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(objectAtIndex:) swizzleSelector:objectsAtIndexSEL]; 37 | [self ff_instancenSwizzleWithClass:originClass originSelector:@selector(getObjects:range:) swizzleSelector:getObjectsRangeSEL]; 38 | 39 | 40 | 41 | Class array0Class = NSClassFromString(@"__NSArray0"); 42 | [self ff_instancenSwizzleWithClass:array0Class originSelector:@selector(objectAtIndex:) swizzleSelector:objectsAtIndexSEL]; 43 | 44 | 45 | 46 | Class singleClass = NSClassFromString(@"__NSSingleObjectArrayI"); 47 | [self ff_instancenSwizzleWithClass:singleClass originSelector:@selector(objectAtIndex:) swizzleSelector:objectsAtIndexSEL]; 48 | [self ff_instancenSwizzleWithClass:singleClass originSelector:@selector(getObjects:range:) swizzleSelector:getObjectsRangeSEL]; 49 | 50 | 51 | Class mutableClass = NSClassFromString(@"NSMutableArray"); 52 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(removeObjectsAtIndexes:) swizzleSelector:@selector(ff_removeObjectsAtIndexes:)]; 53 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(removeObject:inRange:) swizzleSelector:@selector(ff_removeObject:inRange:)]; 54 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(removeObjectAtIndex:) swizzleSelector:@selector(ff_removeObjectAtIndex:)]; 55 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(removeObjectIdenticalTo:inRange:) swizzleSelector:@selector(ff_removeObjectIdenticalTo:inRange:)]; 56 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(replaceObjectsInRange:withObjectsFromArray:) swizzleSelector:@selector(ff_replaceObjectsInRange:withObjectsFromArray:)]; 57 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(replaceObjectsInRange:withObjectsFromArray:range:) swizzleSelector:@selector(ff_replaceObjectsInRange:withObjectsFromArray:range:)]; 58 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(insertObjects:atIndexes:) swizzleSelector:@selector(ff_insertObjects:atIndexes:)]; 59 | [self ff_instancenSwizzleWithClass:mutableClass originSelector:@selector(replaceObjectsAtIndexes:withObjects:) swizzleSelector:@selector(ff_replaceObjectsAtIndexes:withObjects:)]; 60 | 61 | 62 | 63 | Class classM = NSClassFromString(@"__NSArrayM"); 64 | [self ff_instancenSwizzleWithClass:classM originSelector:@selector(insertObject:atIndex:) swizzleSelector:@selector(ff_insertObject:atIndex:)]; 65 | [self ff_instancenSwizzleWithClass:classM originSelector:@selector(removeObjectsInRange:) swizzleSelector:@selector(ff_removeObjectsInRange:)]; 66 | [self ff_instancenSwizzleWithClass:classM originSelector:@selector(replaceObjectAtIndex:withObject:) swizzleSelector:@selector(ff_replaceObjectAtIndex:withObject:)]; 67 | [self ff_instancenSwizzleWithClass:classM originSelector:@selector(exchangeObjectAtIndex:withObjectAtIndex:) swizzleSelector:@selector(ff_exchangeObjectAtIndex:withObjectAtIndex:)]; 68 | [self ff_instancenSwizzleWithClass:classM originSelector:@selector(setObject:atIndexedSubscript:) swizzleSelector:@selector(ff_setObject:atIndexedSubscript:)]; 69 | [self ff_instancenSwizzleWithClass:classM originSelector:@selector(objectAtIndexedSubscript:) swizzleSelector:@selector(ff_objectAtIndexedSubscriptArrayM:)]; 70 | ///< 据说低于11.0交换此方法会导致有键盘显示的地方,此时退到后台会crash? [UIKeyboardLayoutStar release]: message sent to deallocated instance 71 | [self ff_instancenSwizzleWithClass:classM originSelector:@selector(objectAtIndex:) swizzleSelector:objectsAtIndexSEL]; 72 | [self ff_instancenSwizzleWithClass:classM originSelector:@selector(getObjects:range:) swizzleSelector:getObjectsRangeSEL]; 73 | [self ff_instancenSwizzleWithClass:classM originSelector:@selector(removeObjectAtIndex:) swizzleSelector:@selector(ff_removeObjectAtIndexArrayM:)]; 74 | 75 | 76 | Class placeHolderClass = NSClassFromString(@"__NSPlaceholderArray"); 77 | [self ff_instancenSwizzleWithClass:placeHolderClass originSelector:@selector(initWithObjects:count:) swizzleSelector:@selector(ff_initWithObjects:count:)]; 78 | 79 | 80 | Class transferClass = NSClassFromString(@"__NSArrayI_Transfer"); 81 | [self ff_instancenSwizzleWithClass:transferClass originSelector:@selector(objectAtIndex:) swizzleSelector:@selector(ff_objectAtIndex:)]; 82 | [self ff_instancenSwizzleWithClass:transferClass originSelector:@selector(getObjects:range:) swizzleSelector:@selector(ff_getObjects:range:)]; 83 | [self ff_instancenSwizzleWithClass:transferClass originSelector:@selector(objectAtIndexedSubscript:) swizzleSelector:@selector(ff_objectAtIndexedSubscript:)]; 84 | 85 | 86 | Class frozenClass = NSClassFromString(@"__NSFrozenArrayM"); 87 | [self ff_instancenSwizzleWithClass:frozenClass originSelector:@selector(objectAtIndex:) swizzleSelector:@selector(ff_objectAtIndex:)]; 88 | [self ff_instancenSwizzleWithClass:frozenClass originSelector:@selector(getObjects:range:) swizzleSelector:@selector(ff_getObjects:range:)]; 89 | [self ff_instancenSwizzleWithClass:frozenClass originSelector:@selector(objectAtIndexedSubscript:) swizzleSelector:@selector(ff_objectAtIndexedSubscript:)]; 90 | 91 | 92 | Class reverseClass = NSClassFromString(@"__NSArrayReversed"); 93 | [self ff_instancenSwizzleWithClass:reverseClass originSelector:@selector(objectAtIndex:) swizzleSelector:@selector(ff_objectAtIndex:)]; 94 | [self ff_instancenSwizzleWithClass:reverseClass originSelector:@selector(subarrayWithRange:) swizzleSelector:@selector(ff_subarrayWithRange:)]; 95 | [self ff_instancenSwizzleWithClass:reverseClass originSelector:@selector(objectAtIndexedSubscript:) swizzleSelector:@selector(ff_objectAtIndexedSubscript:)]; 96 | 97 | 98 | /* 99 | addObject: 100 | objectAtIndex: 101 | getObjects:range: 102 | insertObject:atIndex: 103 | removeObjectAtIndex: 104 | countByEnumeratingWithState:objects:count: 105 | replaceObjectAtIndex:withObject: 106 | objectAtIndexedSubscript: 107 | // 这个是系统内部用到的,hook后会崩溃on ios11 108 | Class classCFArray__ = NSClassFromString(@"__NSCFArray"); 109 | [self ff_instancenSwizzleWithClass:classCFArray__ originSelector:@selector(objectAtIndex:) swizzleSelector:@selector(ff_objectAtIndex:)]; 110 | */ 111 | } 112 | 113 | - (BOOL)ff_writeToURL:(NSURL *)url error:(NSError * _Nullable __autoreleasing *)error 114 | { 115 | if (!url) { 116 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], url can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; 117 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 118 | return NO; 119 | } 120 | 121 | return [self ff_writeToURL:url error:error]; 122 | } 123 | 124 | - (id)ff_objectAtIndexedSubscript:(NSUInteger)index 125 | { 126 | if (index < self.count) { 127 | return [self ff_objectAtIndexedSubscript:index]; 128 | } 129 | 130 | long count = self.count > 0 ? self.count - 1 : self.count; 131 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], index %ld is out of bounds 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), (long)index, count]; 132 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 133 | return nil; 134 | } 135 | 136 | - (id)ff_objectAtIndexedSubscriptArrayM:(NSUInteger)index 137 | { 138 | if (index < self.count) { 139 | return [self ff_objectAtIndexedSubscriptArrayM:index]; 140 | } 141 | 142 | long count = self.count > 0 ? self.count - 1 : self.count; 143 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], index %ld is out of bounds 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), (long)index, count]; 144 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 145 | return nil; 146 | } 147 | 148 | - (id)ff_objectAtIndex:(NSUInteger)index 149 | { 150 | if (index < self.count) { 151 | return [self ff_objectAtIndex:index]; 152 | } 153 | 154 | long count = self.count > 0 ? self.count - 1 : self.count; 155 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], index %ld is out of bounds 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), (long)index, count]; 156 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 157 | return nil; 158 | } 159 | 160 | - (instancetype)ff_initWithObjects:(const id _Nonnull [_Nullable])objects count:(NSUInteger)cnt 161 | { 162 | NSUInteger realCount = 0; 163 | id _Nonnull __unsafe_unretained realObjects[cnt]; 164 | 165 | // cnt比数组长度大,objects[i]会读取到其他内存对象比如控制器啊什么的,所以cnt要慎重不能乱写 166 | BOOL capture = NO; 167 | for (NSUInteger i = 0; i < cnt; i++) { 168 | if (objects && objects[i]) { 169 | realObjects[realCount] = objects[i]; 170 | realCount++; 171 | } else { 172 | if (!capture) { 173 | capture = YES; 174 | 175 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], the %lu object is nil in 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), (long)i, (long)cnt]; 176 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 177 | } 178 | } 179 | } 180 | 181 | return [self ff_initWithObjects:realObjects count:realCount]; 182 | } 183 | 184 | - (NSArray *)ff_arrayByAddingObject:(id)anObject 185 | { 186 | if (anObject) { 187 | return [self ff_arrayByAddingObject:anObject]; 188 | } 189 | 190 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], object can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 191 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 192 | return self; 193 | } 194 | 195 | ///< 给除了NSArray之外的子类用 196 | - (void)ff_getObjects:(id _Nonnull __unsafe_unretained [_Nonnull])objects range:(NSRange)range 197 | { 198 | if (range.location + range.length <= self.count) { 199 | return [self ff_getObjects:objects range:range]; 200 | } 201 | 202 | long count = self.count > 0 ? self.count - 1 : self.count; 203 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@ is out of bounds 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), NSStringFromRange(range), count]; 204 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 205 | } 206 | 207 | ///< 给NSArray用 208 | - (void)ff_getObjectsForSuperClass:(id _Nonnull __unsafe_unretained [_Nonnull])objects range:(NSRange)range 209 | { 210 | if (range.location + range.length <= self.count) { 211 | return [self ff_getObjectsForSuperClass:objects range:range]; 212 | } 213 | 214 | long count = self.count > 0 ? self.count - 1 : self.count; 215 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@ is out of bounds 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), NSStringFromRange(range), count]; 216 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 217 | } 218 | 219 | - (NSUInteger)ff_indexOfObject:(id)anObject inRange:(NSRange)range 220 | { 221 | if (range.location + range.length <= self.count) { 222 | return [self ff_indexOfObject:anObject inRange:range]; 223 | } 224 | 225 | long count = self.count > 0 ? self.count - 1 : self.count; 226 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@ is out of bounds 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), NSStringFromRange(range), count]; 227 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 228 | return 0; 229 | } 230 | 231 | - (NSUInteger)ff_indexOfObjectIdenticalTo:(id)anObject inRange:(NSRange)range 232 | { 233 | if (range.location + range.length <= self.count) { 234 | return [self ff_indexOfObjectIdenticalTo:anObject inRange:range]; 235 | } 236 | 237 | long count = self.count > 0 ? self.count - 1 : self.count; 238 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@ is out of bounds 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), NSStringFromRange(range), count]; 239 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 240 | return 0; 241 | } 242 | 243 | - (NSArray *)ff_subarrayWithRange:(NSRange)range 244 | { 245 | if (range.location + range.length <= self.count) { 246 | return [self ff_subarrayWithRange:range]; 247 | } 248 | 249 | long count = self.count > 0 ? self.count - 1 : self.count; 250 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@ is out of bounds 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), NSStringFromRange(range), count]; 251 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 252 | return nil; 253 | } 254 | 255 | - (NSArray *)ff_objectsAtIndexes:(NSIndexSet *)indexes 256 | { 257 | if (!indexes) { 258 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], NSIndexset can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 259 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 260 | return nil; 261 | } 262 | 263 | __block BOOL flag = NO; 264 | [indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { 265 | if (idx >= self.count) { 266 | flag = YES; 267 | *stop = YES; 268 | 269 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], idx %ld is out of bounds 0...%ld", NSStringFromClass([self class]), NSStringFromSelector(_cmd), idx, (long)(self.count)]; 270 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 271 | } 272 | }]; 273 | 274 | if (flag) { 275 | return nil; 276 | } 277 | 278 | return [self ff_objectsAtIndexes:indexes]; 279 | } 280 | 281 | - (void)ff_insertObject:(id)anObject atIndex:(NSUInteger)index 282 | { 283 | if (anObject && index <= self.count) { 284 | return [self ff_insertObject:anObject atIndex:index]; 285 | } 286 | 287 | long count = self.count > 0 ? self.count - 1 : self.count; 288 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], object is %@, index is %lu, array bounds is 0...%lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), anObject, (long)index, count]; 289 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 290 | } 291 | 292 | - (void)ff_removeObjectsInRange:(NSRange)range 293 | { 294 | if (range.location + range.length <= self.count) { 295 | return [self ff_removeObjectsInRange:range]; 296 | } 297 | 298 | long count = self.count > 0 ? self.count - 1 : self.count; 299 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@ is out of bounds 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), NSStringFromRange(range), count]; 300 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 301 | } 302 | 303 | - (void)ff_removeObjectsAtIndexes:(NSIndexSet *)indexes 304 | { 305 | if (!indexes) { 306 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], NSIndexset can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 307 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 308 | return; 309 | } 310 | 311 | __block BOOL flag = NO; 312 | [indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { 313 | if (idx >= self.count) { 314 | flag = YES; 315 | *stop = YES; 316 | 317 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], idx %ld is out of bounds 0...%ld", NSStringFromClass([self class]), NSStringFromSelector(_cmd), idx, (long)self.count]; 318 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 319 | } 320 | }]; 321 | 322 | if (flag) { 323 | return; 324 | } 325 | 326 | return [self ff_removeObjectsAtIndexes:indexes]; 327 | } 328 | 329 | - (void)ff_removeObject:(id)anObject inRange:(NSRange)range 330 | { 331 | if (anObject && range.location + range.length <= self.count) { 332 | return [self ff_removeObject:anObject inRange:range]; 333 | } 334 | 335 | long count = self.count > 0 ? self.count - 1 : self.count; 336 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], object is %@, range is %@, array bounds is 0...%lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), anObject, NSStringFromRange(range), count]; 337 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 338 | } 339 | 340 | - (void)ff_removeObjectAtIndex:(NSUInteger)index 341 | { 342 | if (index < self.count) { 343 | return [self ff_removeObjectAtIndex:index]; 344 | } 345 | 346 | long count = self.count > 0 ? self.count - 1 : self.count; 347 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], index %lu is out of bounds 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), (long)index, count]; 348 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 349 | } 350 | 351 | - (void)ff_removeObjectAtIndexArrayM:(NSUInteger)index 352 | { 353 | if (index < self.count) { 354 | return [self ff_removeObjectAtIndexArrayM:index]; 355 | } 356 | 357 | long count = self.count > 0 ? self.count - 1 : self.count; 358 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], index %lu is out of bounds 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), (long)index, count]; 359 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 360 | } 361 | 362 | - (void)ff_replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject 363 | { 364 | if (anObject && index < self.count) { 365 | return [self ff_replaceObjectAtIndex:index withObject:anObject]; 366 | } 367 | 368 | long count = self.count > 0 ? self.count - 1 : self.count; 369 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], object is %@, index is %lu, array bounds is 0...%lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), anObject, (long)index, count]; 370 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 371 | } 372 | 373 | - (void)ff_exchangeObjectAtIndex:(NSUInteger)idx1 withObjectAtIndex:(NSUInteger)idx2 374 | { 375 | if (idx1 < self.count && idx2 < self.count) { 376 | return [self ff_exchangeObjectAtIndex:idx1 withObjectAtIndex:idx2]; 377 | } 378 | 379 | long count = self.count > 0 ? self.count - 1 : self.count; 380 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], index1 is %lu, index2 is %lu, array bounds is 0...%lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), (long)idx1, (long)idx2, count]; 381 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 382 | } 383 | 384 | - (void)ff_removeObjectIdenticalTo:(id)anObject inRange:(NSRange)range 385 | { 386 | if (anObject && range.location + range.length <= self.count) { 387 | return [self ff_removeObjectIdenticalTo:anObject inRange:range]; 388 | } 389 | 390 | long count = self.count > 0 ? self.count - 1 : self.count; 391 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], object is %@, range is %@, array bounds is 0...%lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), anObject, NSStringFromRange(range), count]; 392 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 393 | } 394 | 395 | - (void)ff_setObject:(id)obj atIndexedSubscript:(NSUInteger)idx 396 | { 397 | ///< idx can equal self.count here, it will add to the last of array when equal. 398 | if (obj && idx <= self.count) { 399 | return [self ff_setObject:obj atIndexedSubscript:idx]; 400 | } 401 | 402 | long count = self.count > 0 ? self.count - 1 : self.count; 403 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], object is %@, index is %lu, array bounds is 0...%lu", NSStringFromClass([self class]),NSStringFromSelector(_cmd), obj, (long)idx, count]; 404 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 405 | } 406 | 407 | - (void)ff_replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray *)otherArray 408 | { 409 | if (range.location + range.length <= self.count) { 410 | return [self ff_replaceObjectsInRange:range withObjectsFromArray:otherArray]; 411 | } 412 | 413 | long count = self.count > 0 ? self.count - 1 : self.count; 414 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@ is out of bounds 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), NSStringFromRange(range), count]; 415 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 416 | } 417 | 418 | - (void)ff_replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray *)otherArray range:(NSRange)otherRange 419 | { 420 | if (!otherArray) { 421 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], otherArray can not be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd)]; 422 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 423 | return; 424 | } 425 | 426 | if (range.location + range.length <= self.count) { 427 | if (otherRange.location + otherRange.length <= otherArray.count) { 428 | return [self ff_replaceObjectsInRange:range withObjectsFromArray:otherArray range:otherRange]; 429 | } 430 | } 431 | 432 | long count = self.count > 0 ? self.count - 1 : self.count; 433 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], range %@ or othreRange %@ is out of bounds 0...%ld", NSStringFromClass([self class]),NSStringFromSelector(_cmd), NSStringFromRange(range), NSStringFromRange(otherRange), count]; 434 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 435 | } 436 | 437 | - (void)ff_insertObjects:(NSArray *)objects atIndexes:(NSIndexSet *)indexes 438 | { 439 | if (!objects || !indexes) { 440 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], objects %@ or indexes %@ can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd), objects, indexes]; 441 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 442 | return; 443 | } 444 | 445 | if (objects.count != indexes.count) { 446 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], objects count %lu must equal indexes count %lu", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (long)objects.count, (long)indexes.count-1]; 447 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 448 | return; 449 | } 450 | 451 | 452 | __block BOOL flag = NO; 453 | [indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { 454 | if (idx >= self.count + objects.count) { 455 | flag = YES; 456 | *stop = YES; 457 | 458 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], idx %ld is out of bounds 0...%ld", NSStringFromClass([self class]), NSStringFromSelector(_cmd), idx, (long)(self.count+objects.count)]; 459 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 460 | } 461 | }]; 462 | 463 | if (flag) { 464 | return; 465 | } 466 | 467 | return [self ff_insertObjects:objects atIndexes:indexes]; 468 | } 469 | 470 | - (void)ff_replaceObjectsAtIndexes:(NSIndexSet *)indexes withObjects:(NSArray *)objects 471 | { 472 | if (!objects || !indexes) { 473 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], objects %@ or indexes %@ can not be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd), objects, indexes]; 474 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 475 | return; 476 | } 477 | 478 | if (objects.count != indexes.count) { 479 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], objects count %lu must equal indexes count %lu", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (long)objects.count, (long)indexes.count-1]; 480 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 481 | return; 482 | } 483 | 484 | __block BOOL flag = NO; 485 | [indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { 486 | if (idx >= self.count) { 487 | flag = YES; 488 | *stop = YES; 489 | 490 | NSString *msg = [NSString stringWithFormat:@"+[%@ %@], idx %ld is out of bounds 0...%ld", NSStringFromClass([self class]), NSStringFromSelector(_cmd), idx, (long)(self.count)]; 491 | [[FFExceptionProxy sharedInstance] reportExceptionWithMessage:msg extraDic:nil]; 492 | } 493 | }]; 494 | 495 | if (flag) { 496 | return; 497 | } 498 | 499 | return [self ff_replaceObjectsAtIndexes:indexes withObjects:objects]; 500 | } 501 | 502 | @end 503 | -------------------------------------------------------------------------------- /FFExtension/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 | -------------------------------------------------------------------------------- /FFExtension/SSZTestObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSZTestObject.h 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SSZTestObject : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /FFExtension/SSZTestObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSZTestObject.m 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import "SSZTestObject.h" 10 | 11 | @implementation SSZTestObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /FFExtension/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /FFExtension/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "SSZTestObject.h" 11 | #import 12 | #import "FFManager.h" 13 | 14 | ///< NSSet/NSMutableSet, NSOrderedSet/NSMutableOrderedSet, NSUserDefault 15 | @interface ViewController () 16 | 17 | @end 18 | 19 | @implementation ViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | // Do any additional setup after loading the view, typically from a nib. 24 | 25 | [[FFManager sharedInstance] updateUnrecogziedSelectorClassPrefixs:@[@"SSZ", @"UI"]]; 26 | 27 | [self testUnrecognizedSelector]; 28 | // [self testNSStringHook]; 29 | // [self testArrayHook]; 30 | 31 | 32 | 33 | 34 | // [self testNSDictionaryHook]; 35 | // [self testStringMore]; 36 | // [self testForNSCacheHook]; 37 | // [self testForNSUserDefaultsHook]; 38 | // [self testForNSDataHook]; 39 | // [self testForNSSetHook]; 40 | 41 | // [self logAllMethods:NSClassFromString(@"__NSArrayM")]; 42 | 43 | // [self testExtraTestForString]; 44 | 45 | // [self testExtraForNSArrayHook]; 46 | 47 | 48 | 49 | // [self testForNSAttributedStringHook]; 50 | 51 | /* 52 | NSMutableIndexSet *mutableSet = [NSMutableIndexSet indexSetWithIndex:0]; 53 | [mutableSet addIndex:5]; 54 | 55 | NSMutableArray *mutableArray = [NSMutableArray new]; 56 | [mutableArray addObject:@134]; 57 | [mutableArray addObject:@890]; 58 | [mutableArray addObject:@340]; 59 | [mutableArray addObject:@890]; 60 | [mutableArray addObject:@340]; 61 | [mutableArray replaceObjectsAtIndexes:mutableSet withObjects:@[@12, @34]]; 62 | 63 | 64 | 65 | [mutableArray insertObjects:@[@234534, @909] atIndexes:mutableSet]; 66 | 67 | 68 | [mutableSet addIndex:7]; 69 | [mutableArray objectsAtIndexes:mutableSet]; 70 | */ 71 | 72 | [self testByHufeng]; 73 | } 74 | 75 | - (void)testByHufeng 76 | { 77 | NSString *str = [[NSClassFromString(@"NSConcreteMutableAttributedString") alloc] initWithString:nil]; 78 | NSLog(@"str = %@", str); 79 | 80 | str = [[NSClassFromString(@"NSConcreteAttributedString") alloc] initWithString:nil attributes:nil]; 81 | NSLog(@"str = %@", str); 82 | 83 | str = [[NSClassFromString(@"NSConcreteMutableAttributedString") alloc] initWithString:@"" attributes:@{}]; 84 | str = [[NSClassFromString(@"NSConcreteMutableAttributedString") alloc] initWithString:@"" attributes:nil]; 85 | str = [[NSClassFromString(@"NSConcreteMutableAttributedString") alloc] initWithString:nil attributes:@{}]; 86 | str = [[NSClassFromString(@"NSConcreteMutableAttributedString") alloc] initWithString:nil attributes:nil]; 87 | NSLog(@"str = %@", str); 88 | } 89 | 90 | - (void)testForNSAttributedStringHook 91 | { 92 | // NSArray *tempArray = [[self class] findAllOf:NSAttributedString.class]; 93 | // NSLog(@"tempArray = %@", tempArray); 94 | 95 | UILabel *label = [[UILabel alloc] init]; 96 | label.text = @"wefewf"; 97 | label.font = [UIFont systemFontOfSize:18]; 98 | 99 | [self.view addSubview:label]; 100 | 101 | NSAttributedString *attributed = [[NSAttributedString alloc] initWithString:nil]; 102 | 103 | attributed = [NSAttributedString attributedStringWithAttachment:nil]; 104 | attributed = [[NSAttributedString alloc] initWithAttributedString:nil]; 105 | NSString *str = @"hufeng"; 106 | NSDictionary *dic = @{NSFontAttributeName : [UIFont systemFontOfSize:12]}; 107 | attributed = [[NSAttributedString alloc] initWithString:nil attributes:nil]; 108 | attributed = [[NSAttributedString alloc] initWithString:nil attributes:@{}]; 109 | attributed = [[NSAttributedString alloc] initWithString:str attributes:nil]; 110 | attributed = [[NSAttributedString alloc] initWithString:str attributes:@{}]; 111 | attributed = [[NSAttributedString alloc] initWithURL:nil options:nil documentAttributes:nil error:nil]; 112 | attributed = [[NSAttributedString alloc] initWithFileURL:nil options:nil documentAttributes:nil error:nil]; 113 | attributed = [[NSAttributedString alloc] initWithData:nil options:nil documentAttributes:nil error:nil]; 114 | attributed = [[NSAttributedString alloc] initWithString:str attributes:dic]; 115 | 116 | 117 | NSRange range = NSMakeRange(8, 90); 118 | // [attributed attributesAtIndex:20 effectiveRange:&range]; 119 | // [attributed attribute:NSFontAttributeName atIndex:20 effectiveRange:&range]; 120 | // [attributed attribute:nil atIndex:20 effectiveRange:&range]; 121 | NSRange tempRamge = NSMakeRange(2, 1); 122 | // [attributed attribute:nil atIndex:2 effectiveRange:&tempRamge]; 123 | 124 | [attributed attributedSubstringFromRange:NSMakeRange(90, 90)]; 125 | [attributed isEqualToAttributedString:nil]; 126 | 127 | 128 | NSLog(@"000000000"); 129 | 130 | [NSMutableAttributedString attributedStringWithAttachment:nil]; 131 | [[NSMutableAttributedString alloc] initWithAttributedString:nil]; 132 | 133 | NSMutableAttributedString *mutable = [[NSMutableAttributedString alloc] initWithString:@"hufeng"]; 134 | [mutable replaceCharactersInRange:NSMakeRange(1, 2) withString:nil]; 135 | [mutable replaceCharactersInRange:NSMakeRange(10, 2) withString:@"fwef"]; 136 | [mutable replaceCharactersInRange:NSMakeRange(1, 2) withString:@"wfewf"]; 137 | [mutable replaceCharactersInRange:NSMakeRange(10, 2) withString:nil]; 138 | [mutable replaceCharactersInRange:NSMakeRange(10, 80) withString:@"fwefew"]; 139 | 140 | 141 | [mutable setAttributes:nil range:NSMakeRange(0, 2)]; 142 | [mutable setAttributes:dic range:range]; 143 | [mutable addAttribute:nil value:[UIFont systemFontOfSize:20] range:NSMakeRange(1, 2)]; 144 | [mutable addAttribute:NSFontAttributeName value:nil range:NSMakeRange(1, 2)]; 145 | [mutable addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:20] range:NSMakeRange(1, 20)]; 146 | [mutable addAttribute:nil value:nil range:NSMakeRange(1, 20)]; 147 | [mutable addAttributes:nil range:NSMakeRange(0, 1)]; 148 | [mutable addAttributes:nil range:NSMakeRange(10, 10)]; 149 | [mutable addAttributes:@124 range:NSMakeRange(10, 10)]; 150 | [mutable addAttributes:@"fewfwe" range:NSMakeRange(10, 10)]; 151 | [mutable addAttributes:dic range:NSMakeRange(10, 10)]; 152 | [mutable removeAttribute:nil range:NSMakeRange(1, 1)]; 153 | [mutable removeAttribute:NSFontAttributeName range:NSMakeRange(10, 10)]; 154 | [mutable removeAttribute:nil range:NSMakeRange(10, 10)]; 155 | [mutable replaceCharactersInRange:NSMakeRange(0, 1) withString:nil]; 156 | [mutable replaceCharactersInRange:NSMakeRange(10, 10) withString:nil]; 157 | [mutable replaceCharactersInRange:NSMakeRange(10, 10) withString:@"111"]; 158 | [mutable deleteCharactersInRange:NSMakeRange(10, 12)]; 159 | [mutable appendAttributedString:nil]; 160 | [mutable appendAttributedString:@"r2r23"]; 161 | [mutable insertAttributedString:nil atIndex:0]; 162 | [mutable insertAttributedString:@"fewfwef" atIndex:0]; 163 | [mutable insertAttributedString:attributed atIndex:90]; 164 | 165 | 166 | [mutable setAttributedString:nil]; 167 | [mutable setAttributedString:@123]; 168 | 169 | 170 | // [attributed attributesAtIndex:20 longestEffectiveRange:&range inRange:NSMakeRange(90, 30)]; 171 | 172 | range = NSMakeRange(20, 20); 173 | // [attributed attributesAtIndex:29 longestEffectiveRange:&range inRange:NSMakeRange(10, 21)]; 174 | range = NSMakeRange(2, 20); 175 | // [attributed attribute:NSFontAttributeName atIndex:12 longestEffectiveRange:&range inRange:NSMakeRange(100, 20)]; 176 | // [attributed attribute:NSFontAttributeName atIndex:15 longestEffectiveRange:&range inRange:NSMakeRange(16, 90)]; 177 | NSLog(@"11111"); 178 | 179 | } 180 | 181 | - (void)testExtraForNSArrayHook 182 | { 183 | NSArray *array = @[@1,@2,@3]; 184 | NSMutableArray *mutable = [NSMutableArray arrayWithArray:array]; 185 | 186 | [array objectAtIndex:2]; 187 | [array objectAtIndex:3]; 188 | [array objectAtIndexedSubscript:2]; 189 | [array objectAtIndexedSubscript:3]; 190 | [array objectAtIndexedSubscript:4]; 191 | 192 | [mutable objectAtIndex:2]; 193 | [mutable objectAtIndex:3]; 194 | [mutable objectAtIndexedSubscript:2]; 195 | [mutable objectAtIndexedSubscript:3]; 196 | [mutable objectAtIndexedSubscript:4]; 197 | 198 | [mutable removeObjectAtIndex:3]; 199 | [mutable removeObjectAtIndex:4]; 200 | [mutable removeObjectAtIndex:2]; 201 | 202 | [mutable replaceObjectAtIndex:2 withObject:@"huuu"]; 203 | [mutable replaceObjectAtIndex:3 withObject:@"huuu"]; 204 | [mutable replaceObjectAtIndex:4 withObject:@"huuu"]; 205 | 206 | [mutable exchangeObjectAtIndex:1 withObjectAtIndex:2]; 207 | [mutable exchangeObjectAtIndex:0 withObjectAtIndex:1]; 208 | } 209 | 210 | - (void)testExtraTestForString 211 | { 212 | NSString *timeStr = @"2018-02-24T11:00:00.000Z"; 213 | 214 | NSDate *date = [[self class] dateFromUTC:timeStr]; 215 | NSLog(@"date = %@", date); 216 | 217 | NSString *str = @"hufeng"; 218 | NSMutableString *mutableStr = [NSMutableString stringWithString:str]; 219 | [str substringFromIndex:5]; 220 | [str substringFromIndex:6]; 221 | [str substringToIndex:5]; 222 | [str substringToIndex:6]; 223 | [str substringWithRange:NSMakeRange(0, 5)]; 224 | [str substringWithRange:NSMakeRange(0, 6)]; 225 | [str characterAtIndex:5]; 226 | [str characterAtIndex:6]; 227 | [str rangeOfComposedCharacterSequenceAtIndex:5]; 228 | [str rangeOfComposedCharacterSequenceAtIndex:6]; 229 | 230 | 231 | 232 | [mutableStr substringFromIndex:5]; 233 | [mutableStr substringFromIndex:6]; 234 | [mutableStr substringToIndex:5]; 235 | [mutableStr substringToIndex:6]; 236 | [mutableStr substringWithRange:NSMakeRange(0, 5)]; 237 | [mutableStr substringWithRange:NSMakeRange(0, 6)]; 238 | [mutableStr characterAtIndex:5]; 239 | [mutableStr characterAtIndex:6]; 240 | [mutableStr rangeOfComposedCharacterSequenceAtIndex:5]; 241 | [mutableStr rangeOfComposedCharacterSequenceAtIndex:6]; 242 | 243 | [mutableStr insertString:@"000" atIndex:6]; 244 | [mutableStr insertString:@"111" atIndex:6]; 245 | } 246 | 247 | + (NSDate*)dateFromUTC:(NSString*)utcStr 248 | { 249 | static NSDateFormatter* _utcDateFormatter; 250 | static dispatch_once_t onceToken; 251 | dispatch_once(&onceToken, ^{ 252 | _utcDateFormatter = [[NSDateFormatter alloc] init]; 253 | [_utcDateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZ"]; 254 | }); 255 | 256 | return [_utcDateFormatter dateFromString:utcStr]; 257 | } 258 | 259 | - (void)testForNSSetHook 260 | { 261 | NSSet *set = [NSSet setWithObject:nil]; 262 | set = [NSSet setWithObject:@123]; 263 | NSMutableSet *mutableSet = [NSMutableSet setWithObject:@3435435]; 264 | [set containsObject:nil]; 265 | [mutableSet containsObject:nil]; 266 | [set intersectsSet:nil]; 267 | [set isEqualToSet:nil]; 268 | [set isSubsetOfSet:nil]; 269 | [mutableSet intersectsSet:nil]; 270 | [mutableSet isEqualToSet:nil]; 271 | [mutableSet isSubsetOfSet:nil]; 272 | set = [set setByAddingObject:nil]; 273 | set = [set setByAddingObjectsFromSet:set]; 274 | set = [set setByAddingObjectsFromSet:nil]; 275 | set = [set setByAddingObjectsFromArray:@[]]; 276 | set = [set setByAddingObjectsFromArray:nil]; 277 | 278 | mutableSet = [mutableSet setByAddingObject:nil]; 279 | mutableSet = [mutableSet setByAddingObjectsFromSet:set]; 280 | mutableSet = [mutableSet setByAddingObjectsFromSet:nil]; 281 | mutableSet = [mutableSet setByAddingObjectsFromArray:@[]]; 282 | mutableSet = [mutableSet setByAddingObjectsFromArray:nil]; 283 | 284 | [[NSSet alloc] initWithObjects:nil, nil]; 285 | [[NSSet alloc] initWithSet:nil]; 286 | [[NSSet alloc] initWithSet:nil copyItems:YES]; 287 | [[NSSet alloc] initWithArray:nil]; 288 | 289 | [NSSet setWithObject:nil]; 290 | [NSSet setWithObjects:nil, nil]; 291 | [NSSet setWithArray:nil]; 292 | [NSSet setWithSet:nil]; 293 | [NSSet setWithObjects:nil count:0]; 294 | 295 | 296 | [[NSMutableSet alloc] initWithObjects:nil, nil]; 297 | [[NSMutableSet alloc] initWithSet:nil]; 298 | [[NSMutableSet alloc] initWithSet:nil copyItems:YES]; 299 | [[NSMutableSet alloc] initWithArray:nil]; 300 | 301 | [NSMutableSet setWithObject:nil]; 302 | [NSMutableSet setWithObjects:nil, nil]; 303 | [NSMutableSet setWithArray:nil]; 304 | [NSMutableSet setWithSet:nil]; 305 | [NSMutableSet setWithObjects:nil count:2]; 306 | 307 | [mutableSet addObject:nil]; 308 | [mutableSet removeObject:nil]; 309 | [mutableSet addObjectsFromArray:nil]; 310 | [mutableSet intersectSet:nil]; 311 | [mutableSet minusSet:nil]; 312 | [mutableSet unionSet:nil]; 313 | [mutableSet setSet:nil]; 314 | } 315 | 316 | - (void)testForNSDataHook 317 | { 318 | NSArray *dataClasses = [[self class] findAllOf:[NSData class]]; 319 | // NSLog(@"dataClasses = %@", dataClasses); 320 | 321 | // [self logAllMethods:NSClassFromString(@"NSData")]; 322 | 323 | NSData *data = [@"hufenggersgerg" dataUsingEncoding:NSUTF8StringEncoding]; 324 | NSMutableData *mutableData = [[NSMutableData alloc] initWithData:data]; 325 | NSData *subData = [data subdataWithRange:NSMakeRange(2, 5)]; 326 | const void * str = "fewfewgfewfew"; 327 | 328 | 329 | [mutableData subdataWithRange:NSMakeRange(9, 40)]; 330 | [data rangeOfData:subData options:NSDataSearchAnchored range:NSMakeRange(90, 12)]; 331 | [mutableData rangeOfData:subData options:NSDataSearchAnchored range:NSMakeRange(90, 12)]; 332 | [data rangeOfData:subData options:NSDataSearchAnchored range:NSMakeRange(90, 12)]; 333 | [mutableData rangeOfData:subData options:NSDataSearchAnchored range:NSMakeRange(90, 12)]; 334 | void *buffer = malloc(sizeof(char) * 2); 335 | 336 | [subData getBytes:buffer length:0]; 337 | [subData getBytes:buffer length:4]; 338 | [subData getBytes:buffer length:1]; 339 | [subData getBytes:buffer length:15]; 340 | [subData getBytes:buffer length:150]; 341 | 342 | [data getBytes:buffer length:0]; 343 | [data getBytes:buffer length:4]; 344 | [data getBytes:buffer length:1]; 345 | [data getBytes:buffer length:15]; 346 | [data getBytes:buffer length:150]; 347 | 348 | [mutableData getBytes:buffer length:0]; 349 | [mutableData getBytes:buffer length:4]; 350 | [mutableData getBytes:buffer length:1]; 351 | [mutableData getBytes:buffer length:15]; 352 | [mutableData getBytes:buffer length:150]; 353 | 354 | [data getBytes:NULL length:40]; 355 | [subData getBytes:NULL length:150]; 356 | [mutableData getBytes:NULL length:150]; 357 | 358 | [subData getBytes:buffer range:NSMakeRange(0, 2)]; 359 | [subData getBytes:buffer range:NSMakeRange(5, 2)]; 360 | [subData getBytes:buffer range:NSMakeRange(15, 2)]; 361 | [subData getBytes:buffer range:NSMakeRange(0, 20)]; 362 | [subData getBytes:buffer range:NSMakeRange(0, 200)]; 363 | [subData getBytes:buffer range:NSMakeRange(0, 2000)]; 364 | [subData getBytes:NULL range:NSMakeRange(0, 2)]; 365 | 366 | [data getBytes:buffer range:NSMakeRange(0, 2)]; 367 | [data getBytes:buffer range:NSMakeRange(0, 20)]; 368 | [data getBytes:buffer range:NSMakeRange(0, 200)]; 369 | [data getBytes:buffer range:NSMakeRange(0, 2000)]; 370 | [data getBytes:NULL range:NSMakeRange(0, 2)]; 371 | 372 | [mutableData getBytes:buffer range:NSMakeRange(0, 2)]; 373 | [mutableData getBytes:buffer range:NSMakeRange(0, 20)]; 374 | [mutableData getBytes:buffer range:NSMakeRange(0, 200)]; 375 | [mutableData getBytes:buffer range:NSMakeRange(0, 2000)]; 376 | [mutableData getBytes:NULL range:NSMakeRange(0, 2)]; 377 | 378 | [data subdataWithRange:NSMakeRange(0, 30)]; 379 | [subData subdataWithRange:NSMakeRange(9, 12)]; 380 | [data getBytes:str range:NSMakeRange(3, 90)]; 381 | [data getBytes:str length:40]; 382 | [mutableData getBytes:str range:NSMakeRange(3, 90)]; 383 | [mutableData getBytes:str length:40]; 384 | [mutableData getBytes:nil length:90]; 385 | 386 | 387 | data = [NSData data]; 388 | [data subdataWithRange:NSMakeRange(10, 200)]; 389 | [data rangeOfData:subData options:NSDataSearchAnchored range:NSMakeRange(90, 12)]; 390 | [mutableData rangeOfData:subData options:NSDataSearchAnchored range:NSMakeRange(90, 12)]; 391 | [data rangeOfData:subData options:NSDataSearchAnchored range:NSMakeRange(90, 12)]; 392 | [mutableData rangeOfData:subData options:NSDataSearchAnchored range:NSMakeRange(90, 12)]; 393 | [data getBytes:str range:NSMakeRange(3, 90)]; 394 | [data getBytes:str length:40]; 395 | [mutableData getBytes:str range:NSMakeRange(3, 90)]; 396 | [mutableData getBytes:str length:40]; 397 | [data getBytes:NULL length:40]; 398 | [mutableData getBytes:nil length:90]; 399 | 400 | 401 | 402 | data = [NSData dataWithBytes:str length:10]; 403 | [data subdataWithRange:NSMakeRange(5, 40)]; 404 | [data rangeOfData:subData options:NSDataSearchAnchored range:NSMakeRange(90, 12)]; 405 | [mutableData rangeOfData:subData options:NSDataSearchAnchored range:NSMakeRange(90, 12)]; 406 | [data rangeOfData:subData options:NSDataSearchAnchored range:NSMakeRange(90, 12)]; 407 | [mutableData rangeOfData:subData options:NSDataSearchAnchored range:NSMakeRange(90, 12)]; 408 | [data getBytes:str range:NSMakeRange(3, 90)]; 409 | [data getBytes:str length:40]; 410 | [mutableData getBytes:str range:NSMakeRange(3, 90)]; 411 | [mutableData getBytes:str length:40]; 412 | [data getBytes:NULL length:40]; 413 | [mutableData getBytes:nil length:90]; 414 | 415 | data = [[NSData alloc] initWithBase64EncodedString:nil options:NSDataBase64DecodingIgnoreUnknownCharacters]; 416 | data = [[NSData alloc] initWithBase64EncodedData:nil options:NSDataBase64DecodingIgnoreUnknownCharacters]; 417 | 418 | 419 | [mutableData appendData:nil]; 420 | [mutableData appendBytes:nil length:0]; 421 | [mutableData appendBytes:nil length:100]; 422 | [mutableData appendBytes:str length:10]; 423 | [mutableData appendBytes:str length:100]; 424 | 425 | 426 | [mutableData replaceBytesInRange:NSMakeRange(124, 30) withBytes:str]; 427 | [mutableData replaceBytesInRange:NSMakeRange(1200, 30) withBytes:str]; 428 | [mutableData replaceBytesInRange:NSMakeRange(10, 10) withBytes:NULL]; 429 | [mutableData replaceBytesInRange:NSMakeRange(10, 10) withBytes:nil]; 430 | 431 | [mutableData replaceBytesInRange:NSMakeRange(20, 10) withBytes:str length:60]; 432 | [mutableData replaceBytesInRange:NSMakeRange(20, 10) withBytes:nil length:60]; 433 | [mutableData replaceBytesInRange:NSMakeRange(0, 100) withBytes:nil length:6]; 434 | [mutableData replaceBytesInRange:NSMakeRange(0, 1000) withBytes:str length:600]; 435 | [mutableData resetBytesInRange:NSMakeRange(21, 900)]; 436 | 437 | // [data increaseLengthBy:2034]; 438 | [mutableData increaseLengthBy:353]; 439 | [mutableData setData:nil]; 440 | } 441 | 442 | - (void)testForNSCacheHook 443 | { 444 | NSCache *cache = [NSCache new]; 445 | 446 | [cache removeObjectForKey:nil]; 447 | [cache objectForKey:nil]; 448 | [cache setObject:@4234 forKey:nil]; 449 | [cache setObject:nil forKey:@"ewr"]; 450 | [cache setObject:nil forKey:nil]; 451 | [cache setObject:@"fgf" forKey:nil cost:0]; 452 | 453 | 454 | } 455 | 456 | - (void)testForNSUserDefaultsHook 457 | { 458 | NSUserDefaults *useDefaults = [NSUserDefaults standardUserDefaults]; 459 | [useDefaults setObject:@"fewf" forKey:@"key"]; 460 | [useDefaults setObject:nil forKey:@"key"]; 461 | [useDefaults setObject:@"fewf" forKey:nil]; 462 | [useDefaults objectForKey:nil]; 463 | [useDefaults removeObjectForKey:nil]; 464 | 465 | [useDefaults stringForKey:nil]; 466 | [useDefaults arrayForKey:nil]; 467 | [useDefaults dictionaryForKey:nil]; 468 | [useDefaults dataForKey:nil]; 469 | [useDefaults stringArrayForKey:nil]; 470 | 471 | 472 | [useDefaults integerForKey:nil]; 473 | [useDefaults floatForKey:nil]; 474 | [useDefaults doubleForKey:nil]; 475 | [useDefaults boolForKey:nil]; 476 | [useDefaults URLForKey:nil]; 477 | 478 | [useDefaults setInteger:nil forKey:nil]; 479 | [useDefaults setFloat:0 forKey:nil]; 480 | [useDefaults setBool:0 forKey:nil]; 481 | [useDefaults setURL:nil forKey:nil]; 482 | [useDefaults registerDefaults:nil]; 483 | [useDefaults addSuiteNamed:nil]; 484 | [useDefaults removeSuiteNamed:nil]; 485 | 486 | [useDefaults volatileDomainForName:nil]; 487 | [useDefaults setVolatileDomain:@{} forName:nil]; 488 | [useDefaults setVolatileDomain:nil forName:@"efe"]; 489 | [useDefaults removeVolatileDomainForName:nil]; 490 | 491 | [useDefaults objectIsForcedForKey:nil]; 492 | [useDefaults objectIsForcedForKey:nil inDomain:@"ewf"]; 493 | } 494 | 495 | 496 | 497 | - (void)testStringMore 498 | { 499 | [NSString stringWithUTF8String:nil]; 500 | [NSString stringWithUTF8String:NULL]; 501 | [NSString stringWithCString:nil encoding:NSUTF8StringEncoding]; 502 | [[NSString alloc] initWithUTF8String:nil]; 503 | [[NSString alloc] initWithCString:nil encoding:NSUTF8StringEncoding]; 504 | [[NSString alloc] initWithString:nil]; 505 | 506 | [NSMutableString stringWithCString:nil encoding:NSUTF8StringEncoding]; 507 | [NSMutableString stringWithUTF8String:nil]; 508 | [[NSMutableString alloc] initWithCString:nil encoding:NSUTF8StringEncoding]; 509 | [[NSMutableString alloc] initWithUTF8String:nil]; 510 | [[NSMutableString alloc] initWithString:nil]; 511 | 512 | NSString *str = @"feawfwefwef"; 513 | 514 | // [self logAllMethods:NSClassFromString(@"NSTaggedPointerString")]; 515 | 516 | 517 | [[str copy] substringToIndex:90]; 518 | NSMutableString *mutableStr = [str mutableCopy]; 519 | [mutableStr substringToIndex:90]; 520 | 521 | mutableStr = [NSMutableString stringWithString:@"fewf"]; 522 | [mutableStr substringToIndex:243]; 523 | // [[mutableStr copy] substringToIndex:3434]; 524 | [[mutableStr mutableCopy] substringToIndex:133]; 525 | } 526 | 527 | - (void)testNSDictionaryHook 528 | { 529 | 530 | // NSArray *dicArray = [[self class] findAllOf:[NSDictionary class]]; 531 | // NSLog(@"NSDictionary = %@", dicArray); 532 | // 533 | // NSArray *mutableDic = [[self class] findAllOf:[NSMutableDictionary class]]; 534 | // NSLog(@"NSMutalbeDictionary = %@", mutableDic); 535 | 536 | 537 | NSString *key = @"key"; 538 | NSString *value = @"value"; 539 | // key = nil; 540 | // value = nil; 541 | NSDictionary *dic; 542 | dic = [NSDictionary dictionaryWithObject:value forKey:key]; 543 | dic = @{ key : value }; 544 | 545 | [[dic copy] objectForKey:nil]; 546 | // [[dic copy] setObject:nil forKey:nil]; 547 | [[dic mutableCopy] objectForKey:nil]; 548 | [[dic mutableCopy] setObject:@123 forKey:@"key"]; 549 | [[dic mutableCopy] setObject:nil forKey:@"key"]; 550 | [[dic mutableCopy] setObject:@12 forKey:nil]; 551 | 552 | id objects[1]; 553 | objects[0] = @"vaevewewf"; 554 | 555 | id keys[2]; 556 | keys[0] = @"wef"; 557 | 558 | dic = [NSDictionary dictionaryWithObjects:objects forKeys:keys count:2]; 559 | 560 | dic = [NSDictionary dictionaryWithObjects:@[@2,@2] forKeys:nil]; 561 | dic = [NSMutableDictionary dictionaryWithObjects:@[@2,@2] forKeys:nil]; 562 | [[NSDictionary alloc] initWithDictionary:nil]; 563 | [[NSDictionary alloc] initWithDictionary:nil copyItems:YES]; 564 | [[NSDictionary alloc] initWithObjects:@[@1,@2] forKeys:nil]; 565 | 566 | [NSDictionary sharedKeySetForKeys:@[@"few", @2]]; 567 | // [NSDictionary sharedKeySetForKeys:nil]; 568 | [NSMutableDictionary sharedKeySetForKeys:nil]; 569 | 570 | key = nil; 571 | dic[key]; 572 | [dic objectForKey:nil]; 573 | 574 | NSArray *array = [dic allKeysForObject:value]; 575 | array = [dic allKeysForObject:nil]; 576 | [dic isEqualToDictionary:@{@"wf":@"ewff"}]; 577 | [dic isEqualToDictionary:nil]; 578 | [dic isEqualToDictionary:NULL]; 579 | 580 | [dic writeToFile:nil atomically:NO]; 581 | [dic writeToURL:nil atomically:YES]; 582 | [dic writeToURL:nil error:nil]; 583 | 584 | NSMutableDictionary *mutableDic = [NSMutableDictionary new]; 585 | [mutableDic setObject:@1312 forKey:@"key"]; 586 | [mutableDic setObject:@"ewfefeg" forKeyedSubscript:@"hufeng"]; 587 | 588 | [mutableDic setObject:@123 forKeyedSubscript:nil]; 589 | [mutableDic setObject:nil forKey:@"key"]; 590 | [mutableDic removeObjectForKey:@"hufeng"]; 591 | [mutableDic setObject:nil forKeyedSubscript:@"hufeng"]; 592 | 593 | [mutableDic objectForKey:nil]; 594 | [NSMutableDictionary dictionaryWithSharedKeySet:nil]; 595 | [mutableDic removeObjectForKey:nil]; 596 | [mutableDic removeObjectsForKeys:nil]; 597 | [mutableDic removeObjectsForKeys:@[@"ewf",@322]]; 598 | [mutableDic setDictionary:nil]; 599 | 600 | [mutableDic addEntriesFromDictionary:nil]; 601 | [mutableDic setObject:@2342 forKey:nil]; 602 | [mutableDic setObject:nil forKey:@"fewf"]; 603 | [mutableDic setObject:nil forKey:@234324]; 604 | [mutableDic setObject:nil forKey:nil]; 605 | 606 | // [mutableDic setObject:@"ewf" forKeyedSubscript:nil]; 607 | [mutableDic setObject:nil forKeyedSubscript:@"few"]; 608 | [mutableDic setObject:nil forKeyedSubscript:nil]; 609 | 610 | NSArray *tempArray = @[@"ef"]; 611 | 612 | NSMutableArray *mutableArray = [tempArray mutableCopy]; 613 | 614 | if (@available(iOS 11, *)) { 615 | [mutableDic writeToURL:nil error:nil]; 616 | [tempArray writeToURL:nil error:nil]; 617 | [mutableArray writeToURL:nil error:nil]; 618 | } 619 | 620 | NSMutableData *data = [[NSMutableData alloc] init]; 621 | [data writeToURL:nil atomically:YES]; 622 | [data writeToURL:nil options:NSDataWritingFileProtectionMask error:nil]; 623 | 624 | [dic objectForKeyedSubscript:nil]; 625 | [mutableDic objectForKeyedSubscript:nil]; 626 | 627 | [NSDictionary dictionaryWithDictionary:nil]; 628 | 629 | 630 | } 631 | 632 | - (void)testArrayHook 633 | { 634 | 635 | /*测试 ff_objectAtIndex: 636 | __NSArrayI 637 | __NSArray0 638 | __NSSingleObjectArrayI 639 | __NSArrayM 640 | */ 641 | // SEL sel = NSSelectorFromString(@"objectAtIndex:"); // 642 | SEL sel = NSSelectorFromString(@"getObjects:range:"); 643 | // Method method = class_getInstanceMethod(NSClassFromString(@"__NSArrayI_Transfer"), sel); 644 | // NSLog(@"__NSArrayI_Transfer Method = %p", method); 645 | 646 | NSArray *classes = @[@"__NSArrayI",@"__NSArray0",@"__NSSingleObjectArrayI",@"__NSArrayM"]; 647 | for (NSString *className in classes) { 648 | Class arrClass = NSClassFromString(className); 649 | 650 | 651 | Method originMethod = class_getInstanceMethod(arrClass, sel); 652 | IMP imp = method_getImplementation(originMethod); 653 | NSLog(@"className = %@, Method = %p", arrClass, originMethod); 654 | } 655 | 656 | 657 | NSArray *tempArr = [[self class] findAllOf:[NSArray class]]; 658 | // NSLog(@"NSArray result = %@", tempArr); 659 | 660 | 661 | @[][0]; 662 | [[NSArray new] indexOfObjectIdenticalTo:nil]; 663 | NSArray *array = @[@"1,", @"2"]; 664 | array[3]; 665 | [array objectAtIndex:4]; 666 | [array arrayByAddingObject:nil]; 667 | [array containsObject:nil]; 668 | 669 | array = [[NSArray alloc] initWithObjects:@"1", @"2", nil, nil]; 670 | array = [[NSArray alloc] initWithArray:nil]; 671 | 672 | array[3]; 673 | [array objectAtIndex:4]; 674 | 675 | [array arrayByAddingObject:nil]; 676 | [array arrayByAddingObjectsFromArray:nil]; 677 | 678 | NSString *str = nil; 679 | array = @[@"1", @"2", str, @"4"]; 680 | 681 | [array arrayByAddingObject:nil]; 682 | [array arrayByAddingObjectsFromArray:nil]; 683 | [array containsObject:nil]; 684 | 685 | NSString *temp[5]; 686 | temp[1] = @"111"; 687 | array = [[NSArray alloc] initWithObjects:temp count:4]; 688 | array = [[NSArray alloc] initWithObjects:nil count:4]; 689 | array = [NSArray arrayWithObjects:temp count:4]; 690 | 691 | array = [[NSArray alloc] initWithArray:nil copyItems:YES]; 692 | array = [[NSArray alloc] initWithContentsOfFile:nil]; 693 | array = [[NSArray alloc] initWithContentsOfURL:nil]; 694 | 695 | array = [NSArray array]; 696 | array[3]; 697 | [array objectAtIndex:4]; 698 | [array arrayByAddingObject:nil]; 699 | 700 | array = [NSArray new]; 701 | array[3]; 702 | [array objectAtIndex:4]; 703 | [array arrayByAddingObject:nil]; 704 | 705 | array = [NSArray arrayWithObjects:@"1", @"2", NULL, nil, nil]; 706 | array = [NSArray arrayWithArray:nil]; 707 | array = [NSArray arrayWithObject:nil]; 708 | array = [NSArray arrayWithObjects:temp count:4]; 709 | array = [NSArray arrayWithContentsOfURL:nil]; 710 | array = [NSArray arrayWithContentsOfFile:nil]; 711 | if (@available(iOS 11.0, *)) { 712 | array = [NSArray arrayWithContentsOfURL:nil error:nil]; 713 | } 714 | 715 | 716 | array = @[@"12", @"g", @"uty",@"fwef",@"gerg"]; 717 | [array arrayByAddingObject:nil]; 718 | [array arrayByAddingObjectsFromArray:nil]; 719 | array[4]; 720 | [array objectAtIndex:56]; 721 | [array firstObjectCommonWithArray:nil]; 722 | 723 | [array indexOfObject:nil]; 724 | [array indexOfObject:@23 inRange:NSMakeRange(10, 2)]; 725 | 726 | NSRange range = NSMakeRange(2, 20); 727 | 728 | 729 | __unsafe_unretained id cArray[1]; 730 | 731 | array = @[@"1", @"2", @"3"];//__NSArrayI 732 | array[10]; 733 | [array objectAtIndex:12]; 734 | [array getObjects:cArray range:NSMakeRange(5, 10)]; 735 | [array subarrayWithRange:NSMakeRange(20, 12)]; 736 | [array indexOfObjectIdenticalTo:nil]; 737 | [array indexOfObjectIdenticalTo:nil inRange:NSMakeRange(20, 10)]; 738 | [array isEqualToArray:nil]; 739 | 740 | [array indexOfObject:nil inRange:NSMakeRange(10, 2)]; 741 | array = @[@"1"];//__NSSingleObjectArrayI 742 | array[10]; 743 | [array objectAtIndex:12]; 744 | [array objectAtIndexedSubscript:23]; 745 | [array subarrayWithRange:NSMakeRange(20, 12)]; 746 | [array indexOfObject:nil inRange:NSMakeRange(10, 2)]; 747 | array = @[];//NSArray0 748 | array[10]; 749 | [array objectAtIndex:12]; 750 | [array subarrayWithRange:NSMakeRange(20, 12)]; 751 | [array indexOfObject:nil inRange:NSMakeRange(10, 2)]; 752 | [array indexOfObjectIdenticalTo:nil]; 753 | 754 | [array getObjects:cArray range:range]; 755 | 756 | [array objectAtIndexedSubscript:23]; 757 | NSIndexSet *set = [NSIndexSet indexSetWithIndex:67]; 758 | [array objectsAtIndexes:nil]; 759 | [array objectsAtIndexes:set]; 760 | 761 | NSString *tempStr = [array componentsJoinedByString:nil]; 762 | 763 | 764 | array = [NSMutableArray arrayWithContentsOfURL:nil]; 765 | NSMutableArray *mutableArray = array.mutableCopy; 766 | mutableArray = [NSMutableArray new]; 767 | [mutableArray setObject:@"hufeng" atIndexedSubscript:0]; 768 | array[10]; 769 | [array objectAtIndex:12]; 770 | [mutableArray containsObject:nil]; 771 | [array subarrayWithRange:NSMakeRange(20, 12)]; 772 | mutableArray = [NSMutableArray new]; 773 | [mutableArray subarrayWithRange:NSMakeRange(20, 12)]; // __NSArrayM 774 | mutableArray[10]; 775 | [mutableArray objectAtIndex:12]; 776 | [mutableArray addObject:nil]; 777 | [mutableArray insertObject:nil atIndex:0]; 778 | [mutableArray insertObject:@212 atIndex:12]; 779 | 780 | [mutableArray removeObject:nil]; 781 | [mutableArray removeObjectAtIndex:23]; 782 | [mutableArray removeObjectsInArray:nil]; 783 | [mutableArray removeObjectsAtIndexes:[NSIndexSet indexSetWithIndex:34]]; 784 | [mutableArray removeObjectsInRange:NSMakeRange(20, 12)]; 785 | [mutableArray removeObject:nil inRange:NSMakeRange(12, 12)]; 786 | [mutableArray removeObject:@12 inRange:NSMakeRange(12, 12)]; 787 | 788 | [mutableArray addObjectsFromArray:nil]; 789 | [mutableArray removeObjectIdenticalTo:nil]; 790 | 791 | [mutableArray replaceObjectAtIndex:1 withObject:nil]; 792 | [mutableArray replaceObjectAtIndex:12 withObject:@12]; 793 | 794 | [mutableArray exchangeObjectAtIndex:1 withObjectAtIndex:10]; 795 | [mutableArray removeObjectIdenticalTo:nil]; 796 | [mutableArray removeObjectIdenticalTo:@12 inRange:NSMakeRange(2, 1)]; 797 | [mutableArray addObjectsFromArray:@[@12,@234]]; 798 | [mutableArray setObject:@124354 atIndexedSubscript:2]; 799 | [mutableArray setObject:nil atIndexedSubscript:2]; 800 | [mutableArray setObject:@12 atIndexedSubscript:23]; 801 | [mutableArray replaceObjectsInRange:NSMakeRange(0, 1) withObjectsFromArray:@[@12,@23,@12333]]; 802 | [mutableArray replaceObjectsInRange:NSMakeRange(0, 1) withObjectsFromArray:nil]; 803 | [mutableArray replaceObjectsInRange:NSMakeRange(10, 13) withObjectsFromArray:@[@12,@23,@12333]]; 804 | [mutableArray setArray:nil]; 805 | 806 | [mutableArray replaceObjectsInRange:NSMakeRange(0, 1) withObjectsFromArray:@[@"hufeng", @"kobe"] range:NSMakeRange(0, 2)]; 807 | [mutableArray replaceObjectsInRange:NSMakeRange(0, 1) withObjectsFromArray:@[@"hufeng", @"kobe"] range:NSMakeRange(0, 2)]; 808 | [mutableArray replaceObjectsInRange:NSMakeRange(0, 1) withObjectsFromArray:@[@"hufeng", @"kobe"] range:NSMakeRange(0, 2)]; 809 | [mutableArray replaceObjectsInRange:NSMakeRange(0, 1) withObjectsFromArray:nil range:NSMakeRange(0, 2)]; 810 | 811 | // 数组个数跟indexset的个数要相等 812 | NSMutableIndexSet *mutableSet = [NSMutableIndexSet indexSetWithIndex:1]; 813 | 814 | [mutableArray insertObjects:@[@234534, @909] atIndexes:mutableSet]; 815 | [mutableArray insertObjects:nil atIndexes:[NSIndexSet indexSetWithIndex:0]]; 816 | [mutableArray insertObjects:@[@234,@234534] atIndexes:[NSIndexSet indexSetWithIndex:23]]; 817 | [mutableArray replaceObjectsAtIndexes:mutableSet withObjects:nil]; 818 | 819 | [mutableSet addIndex:20]; 820 | [mutableArray replaceObjectsAtIndexes:nil withObjects:nil]; 821 | [mutableArray replaceObjectsAtIndexes:mutableSet withObjects:@[@"jordan"]]; 822 | mutableArray[20]; 823 | [mutableArray objectAtIndex:23]; 824 | 825 | [mutableArray getObjects:cArray range:range]; 826 | [mutableArray arrayByAddingObject:nil]; 827 | [mutableArray indexOfObject:@23 inRange:NSMakeRange(10, 2)]; 828 | [mutableArray indexOfObjectIdenticalTo:nil inRange:NSMakeRange(20, 10)]; 829 | [mutableArray objectsAtIndexes:nil]; 830 | [mutableArray objectsAtIndexes:mutableSet]; 831 | [mutableArray objectAtIndexedSubscript:45]; 832 | 833 | } 834 | 835 | - (void)testNSStringHook 836 | { 837 | NSArray *tempArray = [[self class] findAllOf:NSString.class]; 838 | NSLog(@"tempArray = %@", tempArray); 839 | 840 | [self logAllMethods:NSClassFromString(@"NSBigMutableString")]; 841 | 842 | 843 | NSString *str = @"nsstring"; 844 | 845 | Class class = NSClassFromString(@"NSBigMutableString"); 846 | NSString *bigStr = [[class alloc] initWithString:str]; 847 | [bigStr substringWithRange:NSMakeRange(10, 30)]; 848 | 849 | 850 | [str characterAtIndex:80]; 851 | 852 | [str hasPrefix:nil]; 853 | [str hasSuffix:nil]; 854 | [str containsString:nil]; 855 | [str rangeOfString:nil]; 856 | [str rangeOfString:nil options:0]; 857 | [str rangeOfComposedCharacterSequenceAtIndex:30]; 858 | [str rangeOfComposedCharacterSequencesForRange:NSMakeRange(90, 90)]; 859 | [str stringByAppendingString:nil]; 860 | [str stringByReplacingOccurrencesOfString:@"s" withString:nil]; 861 | [str stringByReplacingCharactersInRange:NSMakeRange(5, 2) withString:nil]; 862 | 863 | [str substringFromIndex:14]; 864 | [str substringToIndex:80]; 865 | [str substringWithRange:NSMakeRange(90, 90)]; 866 | 867 | 868 | NSArray *arr = [str componentsSeparatedByString:nil]; 869 | 870 | 871 | NSMutableString *mutableStr = [NSMutableString stringWithString:@"mutableString"]; 872 | 873 | [str isEqualToString:nil]; 874 | [mutableStr isEqualToString:nil]; 875 | 876 | [mutableStr hasPrefix:nil]; 877 | [mutableStr hasSuffix:nil]; 878 | [mutableStr containsString:nil]; 879 | [mutableStr rangeOfString:nil]; 880 | [mutableStr rangeOfString:nil options:0]; 881 | [mutableStr rangeOfComposedCharacterSequenceAtIndex:30]; 882 | [mutableStr rangeOfComposedCharacterSequencesForRange:NSMakeRange(90, 90)]; 883 | [mutableStr stringByAppendingString:nil]; 884 | [mutableStr stringByReplacingCharactersInRange:NSMakeRange(50, 2) withString:@"uu"]; 885 | [mutableStr stringByReplacingOccurrencesOfString:@"s" withString:nil]; 886 | [mutableStr stringByReplacingCharactersInRange:NSMakeRange(50, 2) withString:nil]; 887 | 888 | [mutableStr substringFromIndex:14]; 889 | [mutableStr substringToIndex:80]; 890 | [mutableStr substringWithRange:NSMakeRange(90, 90)]; 891 | 892 | [mutableStr characterAtIndex:90]; 893 | [mutableStr replaceCharactersInRange:NSMakeRange(0, 30) withString:nil]; 894 | [mutableStr insertString:nil atIndex:57]; 895 | [mutableStr deleteCharactersInRange:NSMakeRange(5, 20)]; 896 | [mutableStr appendString:nil]; 897 | // [mutableStr appendFormat:nil]; 898 | 899 | NSString *copyStr = [mutableStr copy]; 900 | copyStr = [str copy]; 901 | copyStr = [str mutableCopy]; 902 | copyStr = [mutableStr mutableCopy]; 903 | [copyStr isEqualToString:nil]; 904 | [copyStr substringWithRange:NSMakeRange(90, 90)]; 905 | [copyStr characterAtIndex:90]; 906 | mutableStr.string = nil; 907 | 908 | } 909 | 910 | - (void)testUnrecognizedSelector 911 | { 912 | SSZTestObject *test = [SSZTestObject new]; 913 | 914 | [test performSelector:@selector(efgaegggre:) withObject:@"fwe"]; 915 | 916 | 917 | UIView *view = [UIView new]; 918 | [view performSelector:@selector(wfewfwf) withObject:@"vfwe"]; 919 | 920 | [test performSelector:@selector(class) withObject:@"fwef"]; 921 | 922 | // NSNull *null = [NSNull null]; 923 | // [null performSelector:@selector(length)]; 924 | 925 | // NSArray *tempArr = [[self class] findAllOf:[NSString class]]; 926 | // NSLog(@"NSString result = %@", tempArr); 927 | // 928 | // tempArr = [[self class] findAllOf:[NSNumber class]]; 929 | // NSLog(@"NSNumber result = %@", tempArr); 930 | 931 | // [@"aewgaw" performSelector:@selector(ioghgohepgegwe)]; 932 | // NSMutableString *str = [NSMutableString stringWithString:@"ewfwef"]; 933 | // [str performSelector:@selector(dsknseghggpg)]; 934 | // 935 | // [str performSelector:@selector(efawf:) withObject:@"fewf"]; 936 | 937 | // [self performSelector:@selector(gaegsegreg)]; 938 | } 939 | 940 | 941 | 942 | - (void)didReceiveMemoryWarning { 943 | [super didReceiveMemoryWarning]; 944 | // Dispose of any resources that can be recreated. 945 | } 946 | 947 | + (NSArray *)findAllOf:(Class)defaultClass 948 | { 949 | int count = objc_getClassList(NULL, 0); 950 | if (count <= 0) { 951 | return @[defaultClass]; 952 | } 953 | 954 | NSMutableArray *output = @[].mutableCopy; 955 | Class *classes = (Class *) malloc(sizeof(Class) * count); 956 | objc_getClassList(classes, count); 957 | 958 | for (int i = 0; i < count; ++i) { 959 | Class tempClass = classes[i]; 960 | // 遍历获取所有非NSObject的父类 961 | const char *name = class_getName(tempClass); 962 | NSString *className = [NSString stringWithCString:name encoding:NSUTF8StringEncoding]; 963 | 964 | while(tempClass && ![className isEqualToString:@"NSObject"]){ 965 | if (defaultClass == class_getSuperclass(tempClass)) 966 | { 967 | [output addObject:classes[i]]; 968 | break; 969 | } else { 970 | tempClass = class_getSuperclass(tempClass); 971 | } 972 | } 973 | } 974 | 975 | free(classes); 976 | 977 | return output.copy; 978 | } 979 | 980 | - (void)logAllMethods:(Class)tempClass 981 | { 982 | NSUInteger count = 0; 983 | Method *methods = class_copyMethodList(tempClass, &count); 984 | 985 | for (int i = 0; i < count; i++) 986 | { 987 |         Method method = methods[i]; 988 | 989 |         SEL selector = method_getName(method); 990 | 991 |         NSString *name = NSStringFromSelector(selector); 992 | NSLog(@"array %@ method name = %@", [tempClass class], name); 993 | } 994 | } 995 | 996 | @end 997 | -------------------------------------------------------------------------------- /FFExtension/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // FFExtension 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. 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 | -------------------------------------------------------------------------------- /FFExtensionTests/FFExtensionTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // FFExtensionTests.m 3 | // FFExtensionTests 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FFExtensionTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation FFExtensionTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /FFExtensionTests/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 | -------------------------------------------------------------------------------- /FFExtensionUITests/FFExtensionUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // FFExtensionUITests.m 3 | // FFExtensionUITests 4 | // 5 | // Created by hufeng on 21/9/18. 6 | // Copyright © 2018年 shensz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FFExtensionUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation FFExtensionUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // 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. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /FFExtensionUITests/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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | "Anti 996" License Version 1.0 (Draft) 2 | 3 | Copyright (c) 2019 kobe1941 4 | 5 | Permission is hereby granted to any individual or legal entity 6 | obtaining a copy of this licensed work (including the source code, 7 | documentation and/or related items, hereinafter collectively referred 8 | to as the "licensed work"), free of charge, to deal with the licensed 9 | work for any purpose, including without limitation, the rights to use, 10 | reproduce, modify, prepare derivative works of, distribute, publish 11 | and sublicense the licensed work, subject to the following conditions: 12 | 13 | 1. The individual or the legal entity must conspicuously display, 14 | without modification, this License and the notice on each redistributed 15 | or derivative copy of the Licensed Work. 16 | 17 | 2. The individual or the legal entity must strictly comply with all 18 | applicable laws, regulations, rules and standards of the jurisdiction 19 | relating to labor and employment where the individual is physically 20 | located or where the individual was born or naturalized; or where the 21 | legal entity is registered or is operating (whichever is stricter). In 22 | case that the jurisdiction has no such laws, regulations, rules and 23 | standards or its laws, regulations, rules and standards are 24 | unenforceable, the individual or the legal entity are required to 25 | comply with Core International Labor Standards. 26 | 27 | 3. The individual or the legal entity shall not induce, metaphor or force 28 | its employee(s), whether full-time or part-time, or its independent 29 | contractor(s), in any methods, to agree in oral or written form, to 30 | directly or indirectly restrict, weaken or relinquish his or her 31 | rights or remedies under such laws, regulations, rules and standards 32 | relating to labor and employment as mentioned above, no matter whether 33 | such written or oral agreement are enforceable under the laws of the 34 | said jurisdiction, nor shall such individual or the legal entity 35 | limit, in any methods, the rights of its employee(s) or independent 36 | contractor(s) from reporting or complaining to the copyright holder or 37 | relevant authorities monitoring the compliance of the license about 38 | its violation(s) of the said license. 39 | 40 | THE LICENSED WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 41 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 42 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 43 | IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, 44 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 45 | OTHERWISE, ARISING FROM, OUT OF OR IN ANY WAY CONNECTION WITH THE 46 | LICENSED WORK OR THE USE OR OTHER DEALINGS IN THE LICENSED WORK. 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FFExtension 2 | 3 | ![pod](https://img.shields.io/badge/pod-1.0.7-orange.svg) [![LICENSE](https://img.shields.io/badge/license-Anti%20996-blue.svg)](https://github.com/996icu/996.ICU/blob/master/LICENSE) [![weibo status](https://img.shields.io/badge/weibo-%40Zombie%E7%AC%AC%E4%B8%83%E7%AB%A0-brightgreen.svg)](https://weibo.com/1247589445) 4 | 5 | `ARC` `iOS8.0+` 6 | 7 | 8 | 9 | 友情链接 [![996.icu](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu) 10 | 11 | 12 | 13 | ### [中文介绍](http://www.zoomfeng.com/blog/ffextension.html) 14 | 15 | 主要用于降低iOS APP的常见崩溃,比如数组字典等容器的越界和插入空值,以及可选的unrecognized selector sent to instance这类崩溃。 16 | 17 | 最新版本已经去掉对NSData的hook,会跟高德的SDK用到的某个函数有点冲突,我这边测试用例过不了就直接去掉了。 18 | 19 | 所有被hook的函数都是直接从系统API选取的,基本上能拦截掉绝大部分的常见越界类和空值类数据引起的崩溃。 20 | 21 | KVO,NSTimer强引用这类能用代码规范解决的,本库不做额外的防护。 22 | 23 | To Avoid the classes out of bounds : 24 | 25 | ```objective-c 26 | NSString 27 | NSArray 28 | NSDictionary 29 | NSSet 30 | NSCache 31 | NSUserDefaults 32 | NSAttributedString 33 | ``` 34 | 35 | also , avoid `unrecognized selector sent to instance` crash by user customize. 36 | 37 | 38 | 39 | ### Start 40 | 41 | ```objective-c 42 | $ pod 'FFExtension' 43 | ``` 44 | 45 | 46 | 47 | You need import one head file just like `import ` . 48 | 49 | Then put the code below in your project, maybe in `application:didFinishLaunchingWithOptions:` method, make sure the calss will not dealloc in all app life. 50 | 51 | ```objective-c 52 | __weak typeof(self) weakSelf = self; 53 | [[FFManager sharedInstance] startWorkWithOption:FFHookOptionAll unrecogziedSelectorClassPrefixs:@[@"SSZ"] callBackBlock:^(NSDictionary *exceptionDic) { 54 | [weakSelf reportExecptionToBugly:exceptionDic]; 55 | }]; 56 | ``` 57 | 58 | 如上所述,传入@"SSZ"则可以保护所有以 SSZ 开头的类,避免 `unrecognized selector sent to instance` 这个类型的崩溃。 59 | 60 | 61 | 62 | just like this: 63 | 64 | ```objective-c 65 | #pragma mark - Report Exception To Bugly 66 | - (void)reportExecptionToBugly:(NSDictionary *)exceptionDic 67 | { 68 | if (exceptionDic) { 69 | NSString *name = [exceptionDic objectForKey:FF_Name]; 70 | NSString *reason = [exceptionDic objectForKey:FF_Reason]; 71 | NSDictionary *extraDic = [exceptionDic objectForKey:FF_ExtraDic]; 72 | NSArray *callStack = [exceptionDic objectForKey:FF_CallStackSymbols]; 73 | 74 | [Bugly reportExceptionWithCategory:3 name:name reason:reason callStack:callStack extraInfo:extraDic terminateApp:NO]; 75 | } 76 | } 77 | ``` 78 | 79 | 80 | 81 | `Tips`: classPrefixs is your own calsses that you want to avoid `unrecogzied selector sent to instance ` crash. 82 | 83 | ```objective-c 84 | - (void)startWorkWithOption:(FFHookOption)option unrecogziedSelectorClassPrefixs:(NSArray *)classPrefixs callBackBlock:(FFExceptionBlock)block; 85 | ``` 86 | 87 | 88 | 89 | --- 90 | 91 | 92 | 93 | Safe method for example 94 | 95 | **NSArray** 96 | 97 | ```objective-c 98 | - (BOOL)writeToURL:(NSURL *)url error:(NSError * _Nullable __autoreleasing *)error; 99 | - (id)objectAtIndexedSubscript:(NSUInteger)index; 100 | - (id)objectAtIndexedSubscriptArrayM:(NSUInteger)index; 101 | - (id)objectAtIndex:(NSUInteger)index; 102 | - (instancetype)initWithObjects:(const id _Nonnull [_Nullable])objects count:(NSUInteger)cnt; 103 | - (NSArray *)arrayByAddingObject:(id)anObject; 104 | - (void)getObjects:(id _Nonnull __unsafe_unretained [_Nonnull])objects range:(NSRange)range; 105 | - (NSUInteger)indexOfObject:(id)anObject inRange:(NSRange)range; 106 | - (NSUInteger)indexOfObjectIdenticalTo:(id)anObject inRange:(NSRange)range; 107 | - (NSArray *)subarrayWithRange:(NSRange)range; 108 | - (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes; 109 | - (void)insertObject:(id)anObject atIndex:(NSUInteger)index; 110 | - (void)removeObjectsInRange:(NSRange)range; 111 | - (void)removeObjectsAtIndexes:(NSIndexSet *)indexes; 112 | - (void)removeObject:(id)anObject inRange:(NSRange)range; 113 | - (void)removeObjectAtIndex:(NSUInteger)index; 114 | - (void)removeObjectAtIndexArrayM:(NSUInteger)index; 115 | - (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject; 116 | - (void)exchangeObjectAtIndex:(NSUInteger)idx1 withObjectAtIndex:(NSUInteger)idx2; 117 | - (void)removeObjectIdenticalTo:(id)anObject inRange:(NSRange)range; 118 | - (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx; 119 | - (void)replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray *)otherArray; 120 | - (void)replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray *)otherArray range:(NSRange)otherRange; 121 | - (void)insertObjects:(NSArray *)objects atIndexes:(NSIndexSet *)indexes; 122 | - (void)replaceObjectsAtIndexes:(NSIndexSet *)indexes withObjects:(NSArray *)objects; 123 | ``` 124 | 125 | and so on ... 126 | 127 | 128 | 129 | --- 130 | 131 | 132 | 133 | By use `method swizzle` to do this , no *` try-catch`* . 134 | 135 | 136 | 137 | Have a nice day ! --------------------------------------------------------------------------------