├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── bin └── snake ├── demo ├── demo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── demo.xcscheme ├── demo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── SceneDelegate.h │ ├── SceneDelegate.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── demoTests │ ├── Info.plist │ └── demoTests.m ├── demoUITests │ ├── Info.plist │ └── demoUITests.m └── release │ ├── UnusedClass.json │ ├── UnusedSelector.json │ ├── demo-LinkMap-normal-x86_64.txt │ └── demo.app │ ├── Base.lproj │ ├── LaunchScreen.storyboardc │ │ ├── 01J-lp-oVM-view-Ze5-6b-2t3.nib │ │ ├── Info.plist │ │ └── UIViewController-01J-lp-oVM.nib │ └── Main.storyboardc │ │ ├── BYZ-38-t0r-view-8bC-Xf-vdC.nib │ │ ├── Info.plist │ │ └── UIViewController-BYZ-38-t0r.nib │ ├── Info.plist │ ├── PkgInfo │ ├── _CodeSignature │ └── CodeResources │ └── demo ├── snake.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ └── snake.xcscheme ├── snake ├── algorithm │ ├── utility.cpp │ └── utility.hpp ├── linkmap │ ├── Linkmap.cpp │ └── Linkmap.hpp ├── mach-o │ ├── Arch.cpp │ ├── Arch.h │ ├── Bin.cpp │ └── Bin.h ├── main.cpp ├── objc │ ├── ObjCClass.hpp │ └── ObjCRuntime.h └── output │ ├── Output.cpp │ └── Output.hpp └── storyboard └── storyboard.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/c++,xcode,macos 2 | # Edit at https://www.gitignore.io/?templates=c++,xcode,macos 3 | 4 | ### C++ ### 5 | 6 | # Prerequisites 7 | *.d 8 | 9 | # Compiled Object files 10 | *.slo 11 | *.lo 12 | *.o 13 | *.obj 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Compiled Dynamic libraries 20 | *.so 21 | *.dylib 22 | *.dll 23 | 24 | # Fortran module files 25 | *.mod 26 | *.smod 27 | 28 | # Compiled Static libraries 29 | *.lai 30 | *.la 31 | *.a 32 | *.lib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | #*.app 38 | 39 | ### macOS ### 40 | # General 41 | .DS_Store 42 | .AppleDouble 43 | .LSOverride 44 | 45 | # Icon must end with two \r 46 | Icon 47 | 48 | # Thumbnails 49 | ._* 50 | 51 | # Files that might appear in the root of a volume 52 | .DocumentRevisions-V100 53 | .fseventsd 54 | .Spotlight-V100 55 | .TemporaryItems 56 | .Trashes 57 | .VolumeIcon.icns 58 | .com.apple.timemachine.donotpresent 59 | 60 | # Directories potentially created on remote AFP share 61 | .AppleDB 62 | .AppleDesktop 63 | Network Trash Folder 64 | Temporary Items 65 | .apdisk 66 | 67 | ### Xcode ### 68 | # Xcode 69 | # 70 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 71 | 72 | ## User settings 73 | xcuserdata/ 74 | 75 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 76 | *.xcscmblueprint 77 | *.xccheckout 78 | 79 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 80 | build/ 81 | DerivedData/ 82 | *.moved-aside 83 | *.pbxuser 84 | !default.pbxuser 85 | *.mode1v3 86 | !default.mode1v3 87 | *.mode2v3 88 | !default.mode2v3 89 | *.perspectivev3 90 | !default.perspectivev3 91 | 92 | ## Xcode Patch 93 | *.xcodeproj/* 94 | !*.xcodeproj/project.pbxproj 95 | !*.xcodeproj/xcshareddata/ 96 | !*.xcworkspace/contents.xcworkspacedata 97 | /*.gcno 98 | 99 | ### Xcode Patch ### 100 | **/xcshareddata/WorkspaceSettings.xcsettings 101 | 102 | # End of https://www.gitignore.io/api/c++,xcode,macos 103 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/cxxopts"] 2 | path = vendor/cxxopts 3 | url = https://github.com/jarro2783/cxxopts.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Xinghua 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Snake 2 | 🐍 Snake, Yet Another Mach-O Unused ObjC Selector/Class/Protocol Detector. 3 | 4 | ObjC Metadata 5 | * Classes ✔ 6 | * Protocols ✔ 7 | * Methods ✔ 8 | * Categories ✔ 9 | * Binding Info ✔ 10 | * ObjC-Specific Sections 11 | * `__objc_selrefs` ✔ 12 | * `__objc_superrefs` ✔ 13 | * `__objc_classrefs` ✔ 14 | * `__objc_classlist` ✔ 15 | * `__objc_catlist` ✔ 16 | * `__objc_protolist` ✔ 17 | 18 | See [SnakeKit][1] 19 | ## Features 20 | - [x] Parse Mach-O directly, no Symbols option required in Strip style of Build Settings, __no depends on otool__. 21 | - [x] Unused selectors. 22 | - [x] Unsued classes. 23 | - [x] Unused protocols. 24 | - [x] Duplicate selectors. 25 | - [x] All classes. 26 | - [x] Selector/Classes/Protocols sort by library, and selector size, if Linkmap file provided. 27 | - [x] Fast, a 460.6M binary and a 134.3M linkmap file costs 1.62s(3.7 GHz 6-Core Intel Core i5;40 GB 2667 MHz DDR4). 28 | 29 | ## Homebrew 30 | `brew tap flexih/tap && brew install snake` 31 | 32 | ## How To Use 33 | ``` 34 | Usage: 35 | snake [-dscp] [-l path] path/to/binary ... 36 | 37 | -s, --selector Unused selectors 38 | -c, --class Unused classes 39 | -p, --protocol Unused protocoles 40 | -d, --duplicate Duplicate selectors 41 | -a, --allclass All Classes 42 | -l, --linkmap arg Linkmap file, which has selector size, library name 43 | -j, --json Output json format 44 | --help Print help 45 | ``` 46 | 47 | snake -l path/to/linkmap path/to/binary [-dscpa] 48 | 49 | ## Example 50 | bin/snake -l demo/release/demo-LinkMap-normal-x86\_64.txt demo/release/demo.app/demo -c 51 | ``` 52 | Total Lib Count: 1 53 | Total Unused Class Count: 3 54 | 55 | # demo 56 | 57 | SceneDelegate 58 | UnusedClass 59 | ViewController 60 | ``` 61 | bin/snake -l demo/release/demo-LinkMap-normal-x86\_64.txt demo/release/demo.app/demo -s 62 | ``` 63 | Total Lib Count: 1 64 | Total Class Count: 2 65 | Total Unused Selector: 2 66 | 67 | # demo 68 | 69 | @ UnusedClass 70 | -[UnusedClass unusedMethOfUnusedClass] 6 71 | 72 | @ UsedClass 73 | -[UsedClass unusedMeth] 6 74 | ``` 75 | 76 | ## Storyboard 77 | Used selectors 78 | ``` 79 | python2 storyboard.py path/to/x.storyboard 80 | ``` 81 | 82 | ## Source 83 | 84 | git clone --recursive https://github.com/flexih/Snake.git 85 | 86 | ## Details 87 | 88 | [Objective-C二进制瘦身][2] 89 | 90 | ## Credits 91 | 92 | * [cxxopts][3] 93 | 94 | [1]: https://github.com/flexih/SnakeKit 95 | [2]: https://www.jianshu.com/p/e3cf048c67aa 96 | [3]: https://github.com/jarro2783/cxxopts 97 | -------------------------------------------------------------------------------- /bin/snake: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flexih/Snake/cb6f7ac3ebbf4bdf963ff6800f5716b3387bfbd0/bin/snake -------------------------------------------------------------------------------- /demo/demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 71848D6423E6731C00E55D7E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 71848D6323E6731C00E55D7E /* AppDelegate.m */; }; 11 | 71848D6723E6731C00E55D7E /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 71848D6623E6731C00E55D7E /* SceneDelegate.m */; }; 12 | 71848D6A23E6731C00E55D7E /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 71848D6923E6731C00E55D7E /* ViewController.m */; }; 13 | 71848D6D23E6731C00E55D7E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71848D6B23E6731C00E55D7E /* Main.storyboard */; }; 14 | 71848D6F23E6731C00E55D7E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 71848D6E23E6731C00E55D7E /* Assets.xcassets */; }; 15 | 71848D7223E6731C00E55D7E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71848D7023E6731C00E55D7E /* LaunchScreen.storyboard */; }; 16 | 71848D7523E6731C00E55D7E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 71848D7423E6731C00E55D7E /* main.m */; }; 17 | 71848D7F23E6731C00E55D7E /* demoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 71848D7E23E6731C00E55D7E /* demoTests.m */; }; 18 | 71848D8A23E6731C00E55D7E /* demoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 71848D8923E6731C00E55D7E /* demoUITests.m */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | 71848D7B23E6731C00E55D7E /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = 71848D5723E6731C00E55D7E /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = 71848D5E23E6731C00E55D7E; 27 | remoteInfo = demo; 28 | }; 29 | 71848D8623E6731C00E55D7E /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 71848D5723E6731C00E55D7E /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 71848D5E23E6731C00E55D7E; 34 | remoteInfo = demo; 35 | }; 36 | /* End PBXContainerItemProxy section */ 37 | 38 | /* Begin PBXFileReference section */ 39 | 71848D5F23E6731C00E55D7E /* demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 71848D6223E6731C00E55D7E /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 41 | 71848D6323E6731C00E55D7E /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 42 | 71848D6523E6731C00E55D7E /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; 43 | 71848D6623E6731C00E55D7E /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; 44 | 71848D6823E6731C00E55D7E /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 45 | 71848D6923E6731C00E55D7E /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 46 | 71848D6C23E6731C00E55D7E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 71848D6E23E6731C00E55D7E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 71848D7123E6731C00E55D7E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 71848D7323E6731C00E55D7E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | 71848D7423E6731C00E55D7E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 51 | 71848D7A23E6731C00E55D7E /* demoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = demoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | 71848D7E23E6731C00E55D7E /* demoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = demoTests.m; sourceTree = ""; }; 53 | 71848D8023E6731C00E55D7E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | 71848D8523E6731C00E55D7E /* demoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = demoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 71848D8923E6731C00E55D7E /* demoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = demoUITests.m; sourceTree = ""; }; 56 | 71848D8B23E6731C00E55D7E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 57 | /* End PBXFileReference section */ 58 | 59 | /* Begin PBXFrameworksBuildPhase section */ 60 | 71848D5C23E6731C00E55D7E /* Frameworks */ = { 61 | isa = PBXFrameworksBuildPhase; 62 | buildActionMask = 2147483647; 63 | files = ( 64 | ); 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | 71848D7723E6731C00E55D7E /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | 71848D8223E6731C00E55D7E /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | ); 79 | runOnlyForDeploymentPostprocessing = 0; 80 | }; 81 | /* End PBXFrameworksBuildPhase section */ 82 | 83 | /* Begin PBXGroup section */ 84 | 71848D5623E6731C00E55D7E = { 85 | isa = PBXGroup; 86 | children = ( 87 | 71848D6123E6731C00E55D7E /* demo */, 88 | 71848D7D23E6731C00E55D7E /* demoTests */, 89 | 71848D8823E6731C00E55D7E /* demoUITests */, 90 | 71848D6023E6731C00E55D7E /* Products */, 91 | 71848DB223E673B400E55D7E /* Frameworks */, 92 | ); 93 | sourceTree = ""; 94 | }; 95 | 71848D6023E6731C00E55D7E /* Products */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 71848D5F23E6731C00E55D7E /* demo.app */, 99 | 71848D7A23E6731C00E55D7E /* demoTests.xctest */, 100 | 71848D8523E6731C00E55D7E /* demoUITests.xctest */, 101 | ); 102 | name = Products; 103 | sourceTree = ""; 104 | }; 105 | 71848D6123E6731C00E55D7E /* demo */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 71848D6223E6731C00E55D7E /* AppDelegate.h */, 109 | 71848D6323E6731C00E55D7E /* AppDelegate.m */, 110 | 71848D6523E6731C00E55D7E /* SceneDelegate.h */, 111 | 71848D6623E6731C00E55D7E /* SceneDelegate.m */, 112 | 71848D6823E6731C00E55D7E /* ViewController.h */, 113 | 71848D6923E6731C00E55D7E /* ViewController.m */, 114 | 71848D6B23E6731C00E55D7E /* Main.storyboard */, 115 | 71848D6E23E6731C00E55D7E /* Assets.xcassets */, 116 | 71848D7023E6731C00E55D7E /* LaunchScreen.storyboard */, 117 | 71848D7323E6731C00E55D7E /* Info.plist */, 118 | 71848D7423E6731C00E55D7E /* main.m */, 119 | ); 120 | path = demo; 121 | sourceTree = ""; 122 | }; 123 | 71848D7D23E6731C00E55D7E /* demoTests */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 71848D7E23E6731C00E55D7E /* demoTests.m */, 127 | 71848D8023E6731C00E55D7E /* Info.plist */, 128 | ); 129 | path = demoTests; 130 | sourceTree = ""; 131 | }; 132 | 71848D8823E6731C00E55D7E /* demoUITests */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 71848D8923E6731C00E55D7E /* demoUITests.m */, 136 | 71848D8B23E6731C00E55D7E /* Info.plist */, 137 | ); 138 | path = demoUITests; 139 | sourceTree = ""; 140 | }; 141 | 71848DB223E673B400E55D7E /* Frameworks */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | ); 145 | name = Frameworks; 146 | sourceTree = ""; 147 | }; 148 | /* End PBXGroup section */ 149 | 150 | /* Begin PBXNativeTarget section */ 151 | 71848D5E23E6731C00E55D7E /* demo */ = { 152 | isa = PBXNativeTarget; 153 | buildConfigurationList = 71848D8E23E6731C00E55D7E /* Build configuration list for PBXNativeTarget "demo" */; 154 | buildPhases = ( 155 | 71848D5B23E6731C00E55D7E /* Sources */, 156 | 71848D5C23E6731C00E55D7E /* Frameworks */, 157 | 71848D5D23E6731C00E55D7E /* Resources */, 158 | ); 159 | buildRules = ( 160 | ); 161 | dependencies = ( 162 | ); 163 | name = demo; 164 | productName = demo; 165 | productReference = 71848D5F23E6731C00E55D7E /* demo.app */; 166 | productType = "com.apple.product-type.application"; 167 | }; 168 | 71848D7923E6731C00E55D7E /* demoTests */ = { 169 | isa = PBXNativeTarget; 170 | buildConfigurationList = 71848D9123E6731C00E55D7E /* Build configuration list for PBXNativeTarget "demoTests" */; 171 | buildPhases = ( 172 | 71848D7623E6731C00E55D7E /* Sources */, 173 | 71848D7723E6731C00E55D7E /* Frameworks */, 174 | 71848D7823E6731C00E55D7E /* Resources */, 175 | ); 176 | buildRules = ( 177 | ); 178 | dependencies = ( 179 | 71848D7C23E6731C00E55D7E /* PBXTargetDependency */, 180 | ); 181 | name = demoTests; 182 | productName = demoTests; 183 | productReference = 71848D7A23E6731C00E55D7E /* demoTests.xctest */; 184 | productType = "com.apple.product-type.bundle.unit-test"; 185 | }; 186 | 71848D8423E6731C00E55D7E /* demoUITests */ = { 187 | isa = PBXNativeTarget; 188 | buildConfigurationList = 71848D9423E6731C00E55D7E /* Build configuration list for PBXNativeTarget "demoUITests" */; 189 | buildPhases = ( 190 | 71848D8123E6731C00E55D7E /* Sources */, 191 | 71848D8223E6731C00E55D7E /* Frameworks */, 192 | 71848D8323E6731C00E55D7E /* Resources */, 193 | ); 194 | buildRules = ( 195 | ); 196 | dependencies = ( 197 | 71848D8723E6731C00E55D7E /* PBXTargetDependency */, 198 | ); 199 | name = demoUITests; 200 | productName = demoUITests; 201 | productReference = 71848D8523E6731C00E55D7E /* demoUITests.xctest */; 202 | productType = "com.apple.product-type.bundle.ui-testing"; 203 | }; 204 | /* End PBXNativeTarget section */ 205 | 206 | /* Begin PBXProject section */ 207 | 71848D5723E6731C00E55D7E /* Project object */ = { 208 | isa = PBXProject; 209 | attributes = { 210 | LastUpgradeCheck = 1130; 211 | ORGANIZATIONNAME = flexih; 212 | TargetAttributes = { 213 | 71848D5E23E6731C00E55D7E = { 214 | CreatedOnToolsVersion = 11.3.1; 215 | }; 216 | 71848D7923E6731C00E55D7E = { 217 | CreatedOnToolsVersion = 11.3.1; 218 | TestTargetID = 71848D5E23E6731C00E55D7E; 219 | }; 220 | 71848D8423E6731C00E55D7E = { 221 | CreatedOnToolsVersion = 11.3.1; 222 | TestTargetID = 71848D5E23E6731C00E55D7E; 223 | }; 224 | }; 225 | }; 226 | buildConfigurationList = 71848D5A23E6731C00E55D7E /* Build configuration list for PBXProject "demo" */; 227 | compatibilityVersion = "Xcode 9.3"; 228 | developmentRegion = en; 229 | hasScannedForEncodings = 0; 230 | knownRegions = ( 231 | en, 232 | Base, 233 | ); 234 | mainGroup = 71848D5623E6731C00E55D7E; 235 | productRefGroup = 71848D6023E6731C00E55D7E /* Products */; 236 | projectDirPath = ""; 237 | projectRoot = ""; 238 | targets = ( 239 | 71848D5E23E6731C00E55D7E /* demo */, 240 | 71848D7923E6731C00E55D7E /* demoTests */, 241 | 71848D8423E6731C00E55D7E /* demoUITests */, 242 | ); 243 | }; 244 | /* End PBXProject section */ 245 | 246 | /* Begin PBXResourcesBuildPhase section */ 247 | 71848D5D23E6731C00E55D7E /* Resources */ = { 248 | isa = PBXResourcesBuildPhase; 249 | buildActionMask = 2147483647; 250 | files = ( 251 | 71848D7223E6731C00E55D7E /* LaunchScreen.storyboard in Resources */, 252 | 71848D6F23E6731C00E55D7E /* Assets.xcassets in Resources */, 253 | 71848D6D23E6731C00E55D7E /* Main.storyboard in Resources */, 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | }; 257 | 71848D7823E6731C00E55D7E /* Resources */ = { 258 | isa = PBXResourcesBuildPhase; 259 | buildActionMask = 2147483647; 260 | files = ( 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | }; 264 | 71848D8323E6731C00E55D7E /* Resources */ = { 265 | isa = PBXResourcesBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | ); 269 | runOnlyForDeploymentPostprocessing = 0; 270 | }; 271 | /* End PBXResourcesBuildPhase section */ 272 | 273 | /* Begin PBXSourcesBuildPhase section */ 274 | 71848D5B23E6731C00E55D7E /* Sources */ = { 275 | isa = PBXSourcesBuildPhase; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | 71848D6A23E6731C00E55D7E /* ViewController.m in Sources */, 279 | 71848D6423E6731C00E55D7E /* AppDelegate.m in Sources */, 280 | 71848D7523E6731C00E55D7E /* main.m in Sources */, 281 | 71848D6723E6731C00E55D7E /* SceneDelegate.m in Sources */, 282 | ); 283 | runOnlyForDeploymentPostprocessing = 0; 284 | }; 285 | 71848D7623E6731C00E55D7E /* Sources */ = { 286 | isa = PBXSourcesBuildPhase; 287 | buildActionMask = 2147483647; 288 | files = ( 289 | 71848D7F23E6731C00E55D7E /* demoTests.m in Sources */, 290 | ); 291 | runOnlyForDeploymentPostprocessing = 0; 292 | }; 293 | 71848D8123E6731C00E55D7E /* Sources */ = { 294 | isa = PBXSourcesBuildPhase; 295 | buildActionMask = 2147483647; 296 | files = ( 297 | 71848D8A23E6731C00E55D7E /* demoUITests.m in Sources */, 298 | ); 299 | runOnlyForDeploymentPostprocessing = 0; 300 | }; 301 | /* End PBXSourcesBuildPhase section */ 302 | 303 | /* Begin PBXTargetDependency section */ 304 | 71848D7C23E6731C00E55D7E /* PBXTargetDependency */ = { 305 | isa = PBXTargetDependency; 306 | target = 71848D5E23E6731C00E55D7E /* demo */; 307 | targetProxy = 71848D7B23E6731C00E55D7E /* PBXContainerItemProxy */; 308 | }; 309 | 71848D8723E6731C00E55D7E /* PBXTargetDependency */ = { 310 | isa = PBXTargetDependency; 311 | target = 71848D5E23E6731C00E55D7E /* demo */; 312 | targetProxy = 71848D8623E6731C00E55D7E /* PBXContainerItemProxy */; 313 | }; 314 | /* End PBXTargetDependency section */ 315 | 316 | /* Begin PBXVariantGroup section */ 317 | 71848D6B23E6731C00E55D7E /* Main.storyboard */ = { 318 | isa = PBXVariantGroup; 319 | children = ( 320 | 71848D6C23E6731C00E55D7E /* Base */, 321 | ); 322 | name = Main.storyboard; 323 | sourceTree = ""; 324 | }; 325 | 71848D7023E6731C00E55D7E /* LaunchScreen.storyboard */ = { 326 | isa = PBXVariantGroup; 327 | children = ( 328 | 71848D7123E6731C00E55D7E /* Base */, 329 | ); 330 | name = LaunchScreen.storyboard; 331 | sourceTree = ""; 332 | }; 333 | /* End PBXVariantGroup section */ 334 | 335 | /* Begin XCBuildConfiguration section */ 336 | 71848D8C23E6731C00E55D7E /* Debug */ = { 337 | isa = XCBuildConfiguration; 338 | buildSettings = { 339 | ALWAYS_SEARCH_USER_PATHS = NO; 340 | CLANG_ANALYZER_NONNULL = YES; 341 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 342 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 343 | CLANG_CXX_LIBRARY = "libc++"; 344 | CLANG_ENABLE_MODULES = YES; 345 | CLANG_ENABLE_OBJC_ARC = YES; 346 | CLANG_ENABLE_OBJC_WEAK = YES; 347 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 348 | CLANG_WARN_BOOL_CONVERSION = YES; 349 | CLANG_WARN_COMMA = YES; 350 | CLANG_WARN_CONSTANT_CONVERSION = YES; 351 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 352 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 353 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 354 | CLANG_WARN_EMPTY_BODY = YES; 355 | CLANG_WARN_ENUM_CONVERSION = YES; 356 | CLANG_WARN_INFINITE_RECURSION = YES; 357 | CLANG_WARN_INT_CONVERSION = YES; 358 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 359 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 360 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 361 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 362 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 363 | CLANG_WARN_STRICT_PROTOTYPES = YES; 364 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 365 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 366 | CLANG_WARN_UNREACHABLE_CODE = YES; 367 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 368 | COPY_PHASE_STRIP = NO; 369 | DEBUG_INFORMATION_FORMAT = dwarf; 370 | ENABLE_STRICT_OBJC_MSGSEND = YES; 371 | ENABLE_TESTABILITY = YES; 372 | GCC_C_LANGUAGE_STANDARD = gnu11; 373 | GCC_DYNAMIC_NO_PIC = NO; 374 | GCC_NO_COMMON_BLOCKS = YES; 375 | GCC_OPTIMIZATION_LEVEL = 0; 376 | GCC_PREPROCESSOR_DEFINITIONS = ( 377 | "DEBUG=1", 378 | "$(inherited)", 379 | ); 380 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 381 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 382 | GCC_WARN_UNDECLARED_SELECTOR = YES; 383 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 384 | GCC_WARN_UNUSED_FUNCTION = YES; 385 | GCC_WARN_UNUSED_VARIABLE = YES; 386 | IPHONEOS_DEPLOYMENT_TARGET = 13.2; 387 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 388 | MTL_FAST_MATH = YES; 389 | ONLY_ACTIVE_ARCH = YES; 390 | SDKROOT = iphoneos; 391 | }; 392 | name = Debug; 393 | }; 394 | 71848D8D23E6731C00E55D7E /* Release */ = { 395 | isa = XCBuildConfiguration; 396 | buildSettings = { 397 | ALWAYS_SEARCH_USER_PATHS = NO; 398 | CLANG_ANALYZER_NONNULL = YES; 399 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 400 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 401 | CLANG_CXX_LIBRARY = "libc++"; 402 | CLANG_ENABLE_MODULES = YES; 403 | CLANG_ENABLE_OBJC_ARC = YES; 404 | CLANG_ENABLE_OBJC_WEAK = YES; 405 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 406 | CLANG_WARN_BOOL_CONVERSION = YES; 407 | CLANG_WARN_COMMA = YES; 408 | CLANG_WARN_CONSTANT_CONVERSION = YES; 409 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 410 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 411 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 412 | CLANG_WARN_EMPTY_BODY = YES; 413 | CLANG_WARN_ENUM_CONVERSION = YES; 414 | CLANG_WARN_INFINITE_RECURSION = YES; 415 | CLANG_WARN_INT_CONVERSION = YES; 416 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 417 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 418 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 419 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 420 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 421 | CLANG_WARN_STRICT_PROTOTYPES = YES; 422 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 423 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 424 | CLANG_WARN_UNREACHABLE_CODE = YES; 425 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 426 | COPY_PHASE_STRIP = NO; 427 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 428 | ENABLE_NS_ASSERTIONS = NO; 429 | ENABLE_STRICT_OBJC_MSGSEND = YES; 430 | GCC_C_LANGUAGE_STANDARD = gnu11; 431 | GCC_NO_COMMON_BLOCKS = YES; 432 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 433 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 434 | GCC_WARN_UNDECLARED_SELECTOR = YES; 435 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 436 | GCC_WARN_UNUSED_FUNCTION = YES; 437 | GCC_WARN_UNUSED_VARIABLE = YES; 438 | IPHONEOS_DEPLOYMENT_TARGET = 13.2; 439 | MTL_ENABLE_DEBUG_INFO = NO; 440 | MTL_FAST_MATH = YES; 441 | SDKROOT = iphoneos; 442 | VALIDATE_PRODUCT = YES; 443 | }; 444 | name = Release; 445 | }; 446 | 71848D8F23E6731C00E55D7E /* Debug */ = { 447 | isa = XCBuildConfiguration; 448 | buildSettings = { 449 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 450 | CODE_SIGN_STYLE = Automatic; 451 | INFOPLIST_FILE = demo/Info.plist; 452 | LD_GENERATE_MAP_FILE = YES; 453 | LD_RUNPATH_SEARCH_PATHS = ( 454 | "$(inherited)", 455 | "@executable_path/Frameworks", 456 | ); 457 | OTHER_LDFLAGS = ""; 458 | PRODUCT_BUNDLE_IDENTIFIER = com.flexih.demo; 459 | PRODUCT_NAME = "$(TARGET_NAME)"; 460 | TARGETED_DEVICE_FAMILY = "1,2"; 461 | }; 462 | name = Debug; 463 | }; 464 | 71848D9023E6731C00E55D7E /* Release */ = { 465 | isa = XCBuildConfiguration; 466 | buildSettings = { 467 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 468 | CODE_SIGN_STYLE = Automatic; 469 | INFOPLIST_FILE = demo/Info.plist; 470 | LD_GENERATE_MAP_FILE = YES; 471 | LD_RUNPATH_SEARCH_PATHS = ( 472 | "$(inherited)", 473 | "@executable_path/Frameworks", 474 | ); 475 | OTHER_LDFLAGS = ""; 476 | PRODUCT_BUNDLE_IDENTIFIER = com.flexih.demo; 477 | PRODUCT_NAME = "$(TARGET_NAME)"; 478 | TARGETED_DEVICE_FAMILY = "1,2"; 479 | }; 480 | name = Release; 481 | }; 482 | 71848D9223E6731C00E55D7E /* Debug */ = { 483 | isa = XCBuildConfiguration; 484 | buildSettings = { 485 | BUNDLE_LOADER = "$(TEST_HOST)"; 486 | CODE_SIGN_STYLE = Automatic; 487 | INFOPLIST_FILE = demoTests/Info.plist; 488 | IPHONEOS_DEPLOYMENT_TARGET = 13.2; 489 | LD_RUNPATH_SEARCH_PATHS = ( 490 | "$(inherited)", 491 | "@executable_path/Frameworks", 492 | "@loader_path/Frameworks", 493 | ); 494 | PRODUCT_BUNDLE_IDENTIFIER = com.flexih.demoTests; 495 | PRODUCT_NAME = "$(TARGET_NAME)"; 496 | TARGETED_DEVICE_FAMILY = "1,2"; 497 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/demo.app/demo"; 498 | }; 499 | name = Debug; 500 | }; 501 | 71848D9323E6731C00E55D7E /* Release */ = { 502 | isa = XCBuildConfiguration; 503 | buildSettings = { 504 | BUNDLE_LOADER = "$(TEST_HOST)"; 505 | CODE_SIGN_STYLE = Automatic; 506 | INFOPLIST_FILE = demoTests/Info.plist; 507 | IPHONEOS_DEPLOYMENT_TARGET = 13.2; 508 | LD_RUNPATH_SEARCH_PATHS = ( 509 | "$(inherited)", 510 | "@executable_path/Frameworks", 511 | "@loader_path/Frameworks", 512 | ); 513 | PRODUCT_BUNDLE_IDENTIFIER = com.flexih.demoTests; 514 | PRODUCT_NAME = "$(TARGET_NAME)"; 515 | TARGETED_DEVICE_FAMILY = "1,2"; 516 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/demo.app/demo"; 517 | }; 518 | name = Release; 519 | }; 520 | 71848D9523E6731C00E55D7E /* Debug */ = { 521 | isa = XCBuildConfiguration; 522 | buildSettings = { 523 | CODE_SIGN_STYLE = Automatic; 524 | INFOPLIST_FILE = demoUITests/Info.plist; 525 | LD_RUNPATH_SEARCH_PATHS = ( 526 | "$(inherited)", 527 | "@executable_path/Frameworks", 528 | "@loader_path/Frameworks", 529 | ); 530 | PRODUCT_BUNDLE_IDENTIFIER = com.flexih.demoUITests; 531 | PRODUCT_NAME = "$(TARGET_NAME)"; 532 | TARGETED_DEVICE_FAMILY = "1,2"; 533 | TEST_TARGET_NAME = demo; 534 | }; 535 | name = Debug; 536 | }; 537 | 71848D9623E6731C00E55D7E /* Release */ = { 538 | isa = XCBuildConfiguration; 539 | buildSettings = { 540 | CODE_SIGN_STYLE = Automatic; 541 | INFOPLIST_FILE = demoUITests/Info.plist; 542 | LD_RUNPATH_SEARCH_PATHS = ( 543 | "$(inherited)", 544 | "@executable_path/Frameworks", 545 | "@loader_path/Frameworks", 546 | ); 547 | PRODUCT_BUNDLE_IDENTIFIER = com.flexih.demoUITests; 548 | PRODUCT_NAME = "$(TARGET_NAME)"; 549 | TARGETED_DEVICE_FAMILY = "1,2"; 550 | TEST_TARGET_NAME = demo; 551 | }; 552 | name = Release; 553 | }; 554 | /* End XCBuildConfiguration section */ 555 | 556 | /* Begin XCConfigurationList section */ 557 | 71848D5A23E6731C00E55D7E /* Build configuration list for PBXProject "demo" */ = { 558 | isa = XCConfigurationList; 559 | buildConfigurations = ( 560 | 71848D8C23E6731C00E55D7E /* Debug */, 561 | 71848D8D23E6731C00E55D7E /* Release */, 562 | ); 563 | defaultConfigurationIsVisible = 0; 564 | defaultConfigurationName = Release; 565 | }; 566 | 71848D8E23E6731C00E55D7E /* Build configuration list for PBXNativeTarget "demo" */ = { 567 | isa = XCConfigurationList; 568 | buildConfigurations = ( 569 | 71848D8F23E6731C00E55D7E /* Debug */, 570 | 71848D9023E6731C00E55D7E /* Release */, 571 | ); 572 | defaultConfigurationIsVisible = 0; 573 | defaultConfigurationName = Release; 574 | }; 575 | 71848D9123E6731C00E55D7E /* Build configuration list for PBXNativeTarget "demoTests" */ = { 576 | isa = XCConfigurationList; 577 | buildConfigurations = ( 578 | 71848D9223E6731C00E55D7E /* Debug */, 579 | 71848D9323E6731C00E55D7E /* Release */, 580 | ); 581 | defaultConfigurationIsVisible = 0; 582 | defaultConfigurationName = Release; 583 | }; 584 | 71848D9423E6731C00E55D7E /* Build configuration list for PBXNativeTarget "demoUITests" */ = { 585 | isa = XCConfigurationList; 586 | buildConfigurations = ( 587 | 71848D9523E6731C00E55D7E /* Debug */, 588 | 71848D9623E6731C00E55D7E /* Release */, 589 | ); 590 | defaultConfigurationIsVisible = 0; 591 | defaultConfigurationName = Release; 592 | }; 593 | /* End XCConfigurationList section */ 594 | }; 595 | rootObject = 71848D5723E6731C00E55D7E /* Project object */; 596 | } 597 | -------------------------------------------------------------------------------- /demo/demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo/demo.xcodeproj/xcshareddata/xcschemes/demo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /demo/demo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // demo 4 | // 5 | // Created by flexih on 2020/2/2. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /demo/demo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // demo 4 | // 5 | // Created by flexih on 2020/2/2. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | #pragma mark - UISceneSession lifecycle 25 | 26 | 27 | - (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { 28 | // Called when a new scene session is being created. 29 | // Use this method to select a configuration to create the new scene with. 30 | return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; 31 | } 32 | 33 | 34 | - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { 35 | // Called when the user discards a scene session. 36 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 37 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 38 | } 39 | 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /demo/demo/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 | } -------------------------------------------------------------------------------- /demo/demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /demo/demo/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 | -------------------------------------------------------------------------------- /demo/demo/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 | -------------------------------------------------------------------------------- /demo/demo/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 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /demo/demo/SceneDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.h 3 | // demo 4 | // 5 | // Created by flexih on 2020/2/2. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SceneDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow * window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /demo/demo/SceneDelegate.m: -------------------------------------------------------------------------------- 1 | #import "SceneDelegate.h" 2 | 3 | @interface SceneDelegate () 4 | 5 | @end 6 | 7 | @implementation SceneDelegate 8 | 9 | 10 | - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { 11 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 12 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 13 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 14 | } 15 | 16 | 17 | - (void)sceneDidDisconnect:(UIScene *)scene { 18 | // Called as the scene is being released by the system. 19 | // This occurs shortly after the scene enters the background, or when its session is discarded. 20 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 21 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 22 | } 23 | 24 | 25 | - (void)sceneDidBecomeActive:(UIScene *)scene { 26 | // Called when the scene has moved from an inactive state to an active state. 27 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 28 | } 29 | 30 | 31 | - (void)sceneWillResignActive:(UIScene *)scene { 32 | // Called when the scene will move from an active state to an inactive state. 33 | // This may occur due to temporary interruptions (ex. an incoming phone call). 34 | } 35 | 36 | 37 | - (void)sceneWillEnterForeground:(UIScene *)scene { 38 | // Called as the scene transitions from the background to the foreground. 39 | // Use this method to undo the changes made on entering the background. 40 | } 41 | 42 | 43 | - (void)sceneDidEnterBackground:(UIScene *)scene { 44 | // Called as the scene transitions from the foreground to the background. 45 | // Use this method to save data, release shared resources, and store enough scene-specific state information 46 | // to restore the scene back to its current state. 47 | } 48 | 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /demo/demo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // demo 4 | // 5 | // Created by flexih on 2020/2/2. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /demo/demo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // demo 4 | // 5 | // Created by flexih on 2020/2/2. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @protocol UsedProtocol 12 | 13 | - (void)protoMethUsed; 14 | 15 | @end 16 | 17 | @interface UsedClass : NSObject 18 | 19 | @end 20 | 21 | @implementation UsedClass 22 | 23 | - (void)protoMethUsed { 24 | 25 | } 26 | 27 | - (void)usedMeth { 28 | 29 | } 30 | 31 | - (void)unusedMeth { 32 | 33 | } 34 | 35 | @end 36 | 37 | @interface UnusedClass : NSObject 38 | 39 | @end 40 | 41 | @implementation UnusedClass 42 | 43 | - (void)unusedMethOfUnusedClass { 44 | 45 | } 46 | 47 | @end 48 | 49 | @interface ViewController () 50 | 51 | @end 52 | 53 | @implementation ViewController 54 | 55 | - (void)viewDidLoad { 56 | [super viewDidLoad]; 57 | 58 | UsedClass *uc = [UsedClass new]; 59 | [uc usedMeth]; 60 | } 61 | 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /demo/demo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // demo 4 | // 5 | // Created by flexih on 2020/2/2. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | NSString * appDelegateClassName; 14 | @autoreleasepool { 15 | // Setup code that might create autoreleased objects goes here. 16 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 17 | } 18 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 19 | } 20 | -------------------------------------------------------------------------------- /demo/demoTests/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 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /demo/demoTests/demoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // demoTests.m 3 | // demoTests 4 | // 5 | // Created by flexih on 2020/2/2. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface demoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation demoTests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | } 20 | 21 | - (void)tearDown { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | - (void)testExample { 26 | // This is an example of a functional test case. 27 | // Use XCTAssert and related functions to verify your tests produce the correct results. 28 | } 29 | 30 | - (void)testPerformanceExample { 31 | // This is an example of a performance test case. 32 | [self measureBlock:^{ 33 | // Put the code you want to measure the time of here. 34 | }]; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /demo/demoUITests/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 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /demo/demoUITests/demoUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // demoUITests.m 3 | // demoUITests 4 | // 5 | // Created by flexih on 2020/2/2. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface demoUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation demoUITests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | 20 | // In UI tests it is usually best to stop immediately when a failure occurs. 21 | self.continueAfterFailure = NO; 22 | 23 | // 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. 24 | } 25 | 26 | - (void)tearDown { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | } 29 | 30 | - (void)testExample { 31 | // UI tests must launch the application that they test. 32 | XCUIApplication *app = [[XCUIApplication alloc] init]; 33 | [app launch]; 34 | 35 | // Use recording to get started writing UI tests. 36 | // Use XCTAssert and related functions to verify your tests produce the correct results. 37 | } 38 | 39 | - (void)testLaunchPerformance { 40 | if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) { 41 | // This measures how long it takes to launch your application. 42 | [self measureWithMetrics:@[XCTOSSignpostMetric.applicationLaunchMetric] block:^{ 43 | [[[XCUIApplication alloc] init] launch]; 44 | }]; 45 | } 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /demo/release/UnusedClass.json: -------------------------------------------------------------------------------- 1 | [{"lib":"demo","class":["SceneDelegate","UnusedClass","ViewController"]}] 2 | -------------------------------------------------------------------------------- /demo/release/UnusedSelector.json: -------------------------------------------------------------------------------- 1 | [{"lib":"demo","class":[{"name":"UnusedClass","selector":[{"name":"-[UnusedClass unusedMethOfUnusedClass]","size":6}]},{"name":"UsedClass","selector":[{"name":"-[UsedClass unusedMeth]","size":6}]}]}] 2 | -------------------------------------------------------------------------------- /demo/release/demo-LinkMap-normal-x86_64.txt: -------------------------------------------------------------------------------- 1 | # Path: /Users/flexih/Library/Developer/Xcode/DerivedData/demo-gbmuntmodomouraecyzywewvbfbg/Build/Products/Release-iphonesimulator/demo.app/demo 2 | # Arch: x86_64 3 | # Object files: 4 | [ 0] linker synthesized 5 | [ 1] /Users/flexih/Library/Developer/Xcode/DerivedData/demo-gbmuntmodomouraecyzywewvbfbg/Build/Intermediates.noindex/demo.build/Release-iphonesimulator/demo.build/demo.app-Simulated.xcent 6 | [ 2] /Users/flexih/Library/Developer/Xcode/DerivedData/demo-gbmuntmodomouraecyzywewvbfbg/Build/Intermediates.noindex/demo.build/Release-iphonesimulator/demo.build/Objects-normal/x86_64/ViewController.o 7 | [ 3] /Users/flexih/Library/Developer/Xcode/DerivedData/demo-gbmuntmodomouraecyzywewvbfbg/Build/Intermediates.noindex/demo.build/Release-iphonesimulator/demo.build/Objects-normal/x86_64/AppDelegate.o 8 | [ 4] /Users/flexih/Library/Developer/Xcode/DerivedData/demo-gbmuntmodomouraecyzywewvbfbg/Build/Intermediates.noindex/demo.build/Release-iphonesimulator/demo.build/Objects-normal/x86_64/main.o 9 | [ 5] /Users/flexih/Library/Developer/Xcode/DerivedData/demo-gbmuntmodomouraecyzywewvbfbg/Build/Intermediates.noindex/demo.build/Release-iphonesimulator/demo.build/Objects-normal/x86_64/SceneDelegate.o 10 | [ 6] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.2.sdk/System/Library/Frameworks//Foundation.framework/Foundation.tbd 11 | [ 7] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.2.sdk/usr/lib/libobjc.tbd 12 | [ 8] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.2.sdk/System/Library/Frameworks//UIKit.framework/UIKit.tbd 13 | # Sections: 14 | # Address Size Segment Section 15 | 0x100001235 0x000001D1 __TEXT __text 16 | 0x100001406 0x00000042 __TEXT __stubs 17 | 0x100001448 0x0000007E __TEXT __stub_helper 18 | 0x1000014C6 0x00000093 __TEXT __objc_classname 19 | 0x100001559 0x00000D60 __TEXT __objc_methname 20 | 0x1000022B9 0x00000ADA __TEXT __objc_methtype 21 | 0x100002D93 0x00000090 __TEXT __cstring 22 | 0x100002E23 0x00000170 __TEXT __entitlements 23 | 0x100002F94 0x0000006C __TEXT __unwind_info 24 | 0x100003000 0x00000020 __DATA_CONST __got 25 | 0x100003020 0x00000020 __DATA_CONST __cfstring 26 | 0x100003040 0x00000028 __DATA_CONST __objc_classlist 27 | 0x100003068 0x00000028 __DATA_CONST __objc_protolist 28 | 0x100003090 0x00000008 __DATA_CONST __objc_imageinfo 29 | 0x100004000 0x00000058 __DATA __la_symbol_ptr 30 | 0x100004058 0x00001810 __DATA __objc_const 31 | 0x100005868 0x00000020 __DATA __objc_selrefs 32 | 0x100005888 0x00000018 __DATA __objc_classrefs 33 | 0x1000058A0 0x00000008 __DATA __objc_superrefs 34 | 0x1000058A8 0x00000008 __DATA __objc_ivar 35 | 0x1000058B0 0x00000190 __DATA __objc_data 36 | 0x100005A40 0x000001E8 __DATA __data 37 | # Symbols: 38 | # Address Size File Name 39 | 0x100001235 0x00000006 [ 2] -[UsedClass protoMethUsed] 40 | 0x10000123B 0x00000006 [ 2] -[UsedClass usedMeth] 41 | 0x100001241 0x00000006 [ 2] -[UsedClass unusedMeth] 42 | 0x100001247 0x00000006 [ 2] -[UnusedClass unusedMethOfUnusedClass] 43 | 0x10000124D 0x00000059 [ 2] -[ViewController viewDidLoad] 44 | 0x1000012A6 0x00000008 [ 3] -[AppDelegate application:didFinishLaunchingWithOptions:] 45 | 0x1000012AE 0x0000008E [ 3] -[AppDelegate application:configurationForConnectingSceneSession:options:] 46 | 0x10000133C 0x00000006 [ 3] -[AppDelegate application:didDiscardSceneSessions:] 47 | 0x100001342 0x00000068 [ 4] _main 48 | 0x1000013AA 0x00000006 [ 5] -[SceneDelegate scene:willConnectToSession:options:] 49 | 0x1000013B0 0x00000006 [ 5] -[SceneDelegate sceneDidDisconnect:] 50 | 0x1000013B6 0x00000006 [ 5] -[SceneDelegate sceneDidBecomeActive:] 51 | 0x1000013BC 0x00000006 [ 5] -[SceneDelegate sceneWillResignActive:] 52 | 0x1000013C2 0x00000006 [ 5] -[SceneDelegate sceneWillEnterForeground:] 53 | 0x1000013C8 0x00000006 [ 5] -[SceneDelegate sceneDidEnterBackground:] 54 | 0x1000013CE 0x00000011 [ 5] -[SceneDelegate window] 55 | 0x1000013DF 0x00000014 [ 5] -[SceneDelegate setWindow:] 56 | 0x1000013F3 0x00000013 [ 5] -[SceneDelegate .cxx_destruct] 57 | 0x100001406 0x00000006 [ 6] _NSStringFromClass 58 | 0x10000140C 0x00000006 [ 8] _UIApplicationMain 59 | 0x100001412 0x00000006 [ 7] _objc_alloc 60 | 0x100001418 0x00000006 [ 7] _objc_autoreleasePoolPop 61 | 0x10000141E 0x00000006 [ 7] _objc_autoreleasePoolPush 62 | 0x100001424 0x00000006 [ 7] _objc_autoreleaseReturnValue 63 | 0x10000142A 0x00000006 [ 7] _objc_msgSendSuper2 64 | 0x100001430 0x00000006 [ 7] _objc_opt_class 65 | 0x100001436 0x00000006 [ 7] _objc_opt_new 66 | 0x10000143C 0x00000006 [ 7] _objc_retainAutoreleasedReturnValue 67 | 0x100001442 0x00000006 [ 7] _objc_storeStrong 68 | 0x100001448 0x00000010 [ 0] helper helper 69 | 0x100001458 0x0000000A [ 6] _NSStringFromClass 70 | 0x100001462 0x0000000A [ 7] _objc_alloc 71 | 0x10000146C 0x0000000A [ 7] _objc_autoreleasePoolPop 72 | 0x100001476 0x0000000A [ 7] _objc_autoreleasePoolPush 73 | 0x100001480 0x0000000A [ 7] _objc_autoreleaseReturnValue 74 | 0x10000148A 0x0000000A [ 7] _objc_msgSendSuper2 75 | 0x100001494 0x0000000A [ 7] _objc_opt_class 76 | 0x10000149E 0x0000000A [ 7] _objc_opt_new 77 | 0x1000014A8 0x0000000A [ 7] _objc_retainAutoreleasedReturnValue 78 | 0x1000014B2 0x0000000A [ 7] _objc_storeStrong 79 | 0x1000014BC 0x0000000A [ 8] _UIApplicationMain 80 | 0x1000014C6 0x0000000A [ 2] literal string: UsedClass 81 | 0x1000014D0 0x0000000D [ 2] literal string: UsedProtocol 82 | 0x1000014DD 0x00000009 [ 2] literal string: NSObject 83 | 0x1000014E6 0x0000000C [ 2] literal string: UnusedClass 84 | 0x1000014F2 0x0000000F [ 2] literal string: ViewController 85 | 0x100001501 0x0000000C [ 3] literal string: AppDelegate 86 | 0x10000150D 0x00000016 [ 3] literal string: UIApplicationDelegate 87 | 0x100001523 0x0000000E [ 5] literal string: SceneDelegate 88 | 0x100001531 0x00000016 [ 5] literal string: UIWindowSceneDelegate 89 | 0x100001547 0x00000010 [ 5] literal string: UISceneDelegate 90 | 0x100001557 0x00000002 [ 5] literal string:  91 | 0x100001559 0x00000009 [ 2] literal string: isEqual: 92 | 0x100001562 0x00000006 [ 2] literal string: class 93 | 0x100001568 0x00000005 [ 2] literal string: self 94 | 0x10000156D 0x00000011 [ 2] literal string: performSelector: 95 | 0x10000157E 0x0000001C [ 2] literal string: performSelector:withObject: 96 | 0x10000159A 0x00000027 [ 2] literal string: performSelector:withObject:withObject: 97 | 0x1000015C1 0x00000008 [ 2] literal string: isProxy 98 | 0x1000015C9 0x0000000F [ 2] literal string: isKindOfClass: 99 | 0x1000015D8 0x00000011 [ 2] literal string: isMemberOfClass: 100 | 0x1000015E9 0x00000014 [ 2] literal string: conformsToProtocol: 101 | 0x1000015FD 0x00000014 [ 2] literal string: respondsToSelector: 102 | 0x100001611 0x00000007 [ 2] literal string: retain 103 | 0x100001618 0x00000008 [ 2] literal string: release 104 | 0x100001620 0x0000000C [ 2] literal string: autorelease 105 | 0x10000162C 0x0000000C [ 2] literal string: retainCount 106 | 0x100001638 0x00000005 [ 2] literal string: zone 107 | 0x10000163D 0x00000005 [ 2] literal string: hash 108 | 0x100001642 0x0000000B [ 2] literal string: superclass 109 | 0x10000164D 0x0000000C [ 2] literal string: description 110 | 0x100001659 0x00000011 [ 2] literal string: debugDescription 111 | 0x10000166A 0x0000000E [ 2] literal string: protoMethUsed 112 | 0x100001678 0x00000009 [ 2] literal string: usedMeth 113 | 0x100001681 0x0000000B [ 2] literal string: unusedMeth 114 | 0x10000168C 0x00000018 [ 2] literal string: unusedMethOfUnusedClass 115 | 0x1000016A4 0x0000000C [ 2] literal string: viewDidLoad 116 | 0x1000016B0 0x00000005 [ 3] literal string: role 117 | 0x1000016B5 0x0000001A [ 3] literal string: initWithName:sessionRole: 118 | 0x1000016CF 0x0000001F [ 3] literal string: applicationDidFinishLaunching: 119 | 0x1000016EE 0x0000002C [ 3] literal string: application:willFinishLaunchingWithOptions: 120 | 0x10000171A 0x0000002B [ 3] literal string: application:didFinishLaunchingWithOptions: 121 | 0x100001745 0x0000001C [ 3] literal string: applicationDidBecomeActive: 122 | 0x100001761 0x0000001D [ 3] literal string: applicationWillResignActive: 123 | 0x10000177E 0x0000001B [ 3] literal string: application:handleOpenURL: 124 | 0x100001799 0x00000032 [ 3] literal string: application:openURL:sourceApplication:annotation: 125 | 0x1000017CB 0x0000001D [ 3] literal string: application:openURL:options: 126 | 0x1000017E8 0x00000024 [ 3] literal string: applicationDidReceiveMemoryWarning: 127 | 0x10000180C 0x0000001A [ 3] literal string: applicationWillTerminate: 128 | 0x100001826 0x00000022 [ 3] literal string: applicationSignificantTimeChange: 129 | 0x100001848 0x00000035 [ 3] literal string: application:willChangeStatusBarOrientation:duration: 130 | 0x10000187D 0x0000002B [ 3] literal string: application:didChangeStatusBarOrientation: 131 | 0x1000018A8 0x00000026 [ 3] literal string: application:willChangeStatusBarFrame: 132 | 0x1000018CE 0x00000025 [ 3] literal string: application:didChangeStatusBarFrame: 133 | 0x1000018F3 0x00000031 [ 3] literal string: application:didRegisterUserNotificationSettings: 134 | 0x100001924 0x0000003E [ 3] literal string: application:didRegisterForRemoteNotificationsWithDeviceToken: 135 | 0x100001962 0x0000003E [ 3] literal string: application:didFailToRegisterForRemoteNotificationsWithError: 136 | 0x1000019A0 0x0000002A [ 3] literal string: application:didReceiveRemoteNotification: 137 | 0x1000019CA 0x00000029 [ 3] literal string: application:didReceiveLocalNotification: 138 | 0x1000019F3 0x0000004F [ 3] literal string: application:handleActionWithIdentifier:forLocalNotification:completionHandler: 139 | 0x100001A42 0x00000061 [ 3] literal string: application:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler: 140 | 0x100001AA3 0x00000050 [ 3] literal string: application:handleActionWithIdentifier:forRemoteNotification:completionHandler: 141 | 0x100001AF3 0x00000060 [ 3] literal string: application:handleActionWithIdentifier:forLocalNotification:withResponseInfo:completionHandler: 142 | 0x100001B53 0x00000041 [ 3] literal string: application:didReceiveRemoteNotification:fetchCompletionHandler: 143 | 0x100001B94 0x0000002F [ 3] literal string: application:performFetchWithCompletionHandler: 144 | 0x100001BC3 0x0000003C [ 3] literal string: application:performActionForShortcutItem:completionHandler: 145 | 0x100001BFF 0x00000043 [ 3] literal string: application:handleEventsForBackgroundURLSession:completionHandler: 146 | 0x100001C42 0x00000032 [ 3] literal string: application:handleWatchKitExtensionRequest:reply: 147 | 0x100001C74 0x0000002D [ 3] literal string: applicationShouldRequestHealthAuthorization: 148 | 0x100001CA1 0x0000002C [ 3] literal string: application:handleIntent:completionHandler: 149 | 0x100001CCD 0x0000001F [ 3] literal string: applicationDidEnterBackground: 150 | 0x100001CEC 0x00000020 [ 3] literal string: applicationWillEnterForeground: 151 | 0x100001D0C 0x0000002F [ 3] literal string: applicationProtectedDataWillBecomeUnavailable: 152 | 0x100001D3B 0x0000002C [ 3] literal string: applicationProtectedDataDidBecomeAvailable: 153 | 0x100001D67 0x00000035 [ 3] literal string: application:supportedInterfaceOrientationsForWindow: 154 | 0x100001D9C 0x00000031 [ 3] literal string: application:shouldAllowExtensionPointIdentifier: 155 | 0x100001DCD 0x0000003F [ 3] literal string: application:viewControllerWithRestorationIdentifierPath:coder: 156 | 0x100001E0C 0x0000002E [ 3] literal string: application:shouldSaveSecureApplicationState: 157 | 0x100001E3A 0x00000031 [ 3] literal string: application:shouldRestoreSecureApplicationState: 158 | 0x100001E6B 0x00000030 [ 3] literal string: application:willEncodeRestorableStateWithCoder: 159 | 0x100001E9B 0x0000002F [ 3] literal string: application:didDecodeRestorableStateWithCoder: 160 | 0x100001ECA 0x00000028 [ 3] literal string: application:shouldSaveApplicationState: 161 | 0x100001EF2 0x0000002B [ 3] literal string: application:shouldRestoreApplicationState: 162 | 0x100001F1D 0x0000002E [ 3] literal string: application:willContinueUserActivityWithType: 163 | 0x100001F4B 0x00000035 [ 3] literal string: application:continueUserActivity:restorationHandler: 164 | 0x100001F80 0x00000039 [ 3] literal string: application:didFailToContinueUserActivityWithType:error: 165 | 0x100001FB9 0x00000023 [ 3] literal string: application:didUpdateUserActivity: 166 | 0x100001FDC 0x00000034 [ 3] literal string: application:userDidAcceptCloudKitShareWithMetadata: 167 | 0x100002010 0x0000003C [ 3] literal string: application:configurationForConnectingSceneSession:options: 168 | 0x10000204C 0x00000025 [ 3] literal string: application:didDiscardSceneSessions: 169 | 0x100002071 0x00000007 [ 3] literal string: window 170 | 0x100002078 0x0000000B [ 3] literal string: setWindow: 171 | 0x100002083 0x00000024 [ 5] literal string: scene:willConnectToSession:options: 172 | 0x1000020A7 0x00000014 [ 5] literal string: sceneDidDisconnect: 173 | 0x1000020BB 0x00000016 [ 5] literal string: sceneDidBecomeActive: 174 | 0x1000020D1 0x00000017 [ 5] literal string: sceneWillResignActive: 175 | 0x1000020E8 0x0000001A [ 5] literal string: sceneWillEnterForeground: 176 | 0x100002102 0x00000019 [ 5] literal string: sceneDidEnterBackground: 177 | 0x10000211B 0x00000017 [ 5] literal string: scene:openURLContexts: 178 | 0x100002132 0x00000022 [ 5] literal string: stateRestorationActivityForScene: 179 | 0x100002154 0x00000028 [ 5] literal string: scene:willContinueUserActivityWithType: 180 | 0x10000217C 0x0000001C [ 5] literal string: scene:continueUserActivity: 181 | 0x100002198 0x00000033 [ 5] literal string: scene:didFailToContinueUserActivityWithType:error: 182 | 0x1000021CB 0x0000001D [ 5] literal string: scene:didUpdateUserActivity: 183 | 0x1000021E8 0x0000004B [ 5] literal string: windowScene:didUpdateCoordinateSpace:interfaceOrientation:traitCollection: 184 | 0x100002233 0x0000003C [ 5] literal string: windowScene:performActionForShortcutItem:completionHandler: 185 | 0x10000226F 0x00000034 [ 5] literal string: windowScene:userDidAcceptCloudKitShareWithMetadata: 186 | 0x1000022A3 0x0000000E [ 5] literal string: .cxx_destruct 187 | 0x1000022B1 0x00000008 [ 5] literal string: _window 188 | 0x1000022B9 0x0000000B [ 2] literal string: B24@0:8@16 189 | 0x1000022C4 0x00000008 [ 2] literal string: #16@0:8 190 | 0x1000022CC 0x00000008 [ 2] literal string: @16@0:8 191 | 0x1000022D4 0x0000000B [ 2] literal string: @24@0:8:16 192 | 0x1000022DF 0x0000000E [ 2] literal string: @32@0:8:16@24 193 | 0x1000022ED 0x00000011 [ 2] literal string: @40@0:8:16@24@32 194 | 0x1000022FE 0x00000008 [ 2] literal string: B16@0:8 195 | 0x100002306 0x0000000B [ 2] literal string: B24@0:8#16 196 | 0x100002311 0x0000000B [ 2] literal string: B24@0:8:16 197 | 0x10000231C 0x00000009 [ 2] literal string: Vv16@0:8 198 | 0x100002325 0x00000008 [ 2] literal string: Q16@0:8 199 | 0x10000232D 0x00000012 [ 2] literal string: ^{_NSZone=}16@0:8 200 | 0x10000233F 0x00000015 [ 2] literal string: B24@0:8@"Protocol"16 201 | 0x100002354 0x00000012 [ 2] literal string: @"NSString"16@0:8 202 | 0x100002366 0x00000008 [ 2] literal string: v16@0:8 203 | 0x10000236E 0x0000000B [ 3] literal string: v24@0:8@16 204 | 0x100002379 0x0000000E [ 3] literal string: B32@0:8@16@24 205 | 0x100002387 0x00000014 [ 3] literal string: B48@0:8@16@24@32@40 206 | 0x10000239B 0x00000011 [ 3] literal string: B40@0:8@16@24@32 207 | 0x1000023AC 0x00000011 [ 3] literal string: v40@0:8@16q24d32 208 | 0x1000023BD 0x0000000E [ 3] literal string: v32@0:8@16q24 209 | 0x1000023CB 0x0000002D [ 3] literal string: v56@0:8@16{CGRect={CGPoint=dd}{CGSize=dd}}24 210 | 0x1000023F8 0x0000000E [ 3] literal string: v32@0:8@16@24 211 | 0x100002406 0x00000015 [ 3] literal string: v48@0:8@16@24@32@?40 212 | 0x10000241B 0x00000018 [ 3] literal string: v56@0:8@16@24@32@40@?48 213 | 0x100002433 0x00000012 [ 3] literal string: v40@0:8@16@24@?32 214 | 0x100002445 0x0000000F [ 3] literal string: v32@0:8@16@?24 215 | 0x100002454 0x0000000E [ 3] literal string: Q32@0:8@16@24 216 | 0x100002462 0x00000011 [ 3] literal string: @40@0:8@16@24@32 217 | 0x100002473 0x00000012 [ 3] literal string: B40@0:8@16@24@?32 218 | 0x100002485 0x00000011 [ 3] literal string: v40@0:8@16@24@32 219 | 0x100002496 0x0000001A [ 3] literal string: v24@0:8@"UIApplication"16 220 | 0x1000024B0 0x0000002B [ 3] literal string: B32@0:8@"UIApplication"16@"NSDictionary"24 221 | 0x1000024DB 0x00000024 [ 3] literal string: B32@0:8@"UIApplication"16@"NSURL"24 222 | 0x1000024FF 0x00000034 [ 3] literal string: B48@0:8@"UIApplication"16@"NSURL"24@"NSString"32@40 223 | 0x100002533 0x00000035 [ 3] literal string: B40@0:8@"UIApplication"16@"NSURL"24@"NSDictionary"32 224 | 0x100002568 0x00000020 [ 3] literal string: v40@0:8@"UIApplication"16q24d32 225 | 0x100002588 0x0000001D [ 3] literal string: v32@0:8@"UIApplication"16q24 226 | 0x1000025A5 0x0000003C [ 3] literal string: v56@0:8@"UIApplication"16{CGRect={CGPoint=dd}{CGSize=dd}}24 227 | 0x1000025E1 0x00000039 [ 3] literal string: v32@0:8@"UIApplication"16@"UIUserNotificationSettings"24 228 | 0x10000261A 0x00000025 [ 3] literal string: v32@0:8@"UIApplication"16@"NSData"24 229 | 0x10000263F 0x00000026 [ 3] literal string: v32@0:8@"UIApplication"16@"NSError"24 230 | 0x100002665 0x0000002B [ 3] literal string: v32@0:8@"UIApplication"16@"NSDictionary"24 231 | 0x100002690 0x00000032 [ 3] literal string: v32@0:8@"UIApplication"16@"UILocalNotification"24 232 | 0x1000026C2 0x00000048 [ 3] literal string: v48@0:8@"UIApplication"16@"NSString"24@"UILocalNotification"32@?40 233 | 0x10000270A 0x00000052 [ 3] literal string: v56@0:8@"UIApplication"16@"NSString"24@"NSDictionary"32@"NSDictionary"40@?48 234 | 0x10000275C 0x00000041 [ 3] literal string: v48@0:8@"UIApplication"16@"NSString"24@"NSDictionary"32@?40 235 | 0x10000279D 0x00000059 [ 3] literal string: v56@0:8@"UIApplication"16@"NSString"24@"UILocalNotification"32@"NSDictionary"40@?48 236 | 0x1000027F6 0x00000035 [ 3] literal string: v40@0:8@"UIApplication"16@"NSDictionary"24@?32 237 | 0x10000282B 0x00000024 [ 3] literal string: v32@0:8@"UIApplication"16@?24 238 | 0x10000284F 0x00000042 [ 3] literal string: v40@0:8@"UIApplication"16@"UIApplicationShortcutItem"24@?32 239 | 0x100002891 0x00000030 [ 3] literal string: v40@0:8@"UIApplication"16@"NSString"24@?32 240 | 0x1000028C1 0x00000043 [ 3] literal string: v40@0:8@"UIApplication"16@"NSDictionary"24@?32 241 | 0x100002904 0x00000043 [ 3] literal string: v40@0:8@"UIApplication"16@"INIntent"24@?32 242 | 0x100002947 0x00000027 [ 3] literal string: Q32@0:8@"UIApplication"16@"UIWindow"24 243 | 0x10000296E 0x00000027 [ 3] literal string: B32@0:8@"UIApplication"16@"NSString"24 244 | 0x100002995 0x00000044 [ 3] literal string: @"UIViewController"40@0:8@"UIApplication"16@"NSArray"24@"NSCoder"32 245 | 0x1000029D9 0x00000026 [ 3] literal string: B32@0:8@"UIApplication"16@"NSCoder"24 246 | 0x1000029FF 0x00000026 [ 3] literal string: v32@0:8@"UIApplication"16@"NSCoder"24 247 | 0x100002A25 0x00000040 [ 3] literal string: B40@0:8@"UIApplication"16@"NSUserActivity"24@?32 248 | 0x100002A65 0x00000033 [ 3] literal string: v40@0:8@"UIApplication"16@"NSString"24@"NSError"32 249 | 0x100002A98 0x0000002D [ 3] literal string: v32@0:8@"UIApplication"16@"NSUserActivity"24 250 | 0x100002AC5 0x0000002E [ 3] literal string: v32@0:8@"UIApplication"16@"CKShareMetadata"24 251 | 0x100002AF3 0x00000060 [ 3] literal string: @"UISceneConfiguration"40@0:8@"UIApplication"16@"UISceneSession"24@"UISceneConnectionOptions"32 252 | 0x100002B53 0x00000024 [ 3] literal string: v32@0:8@"UIApplication"16@"NSSet"24 253 | 0x100002B77 0x00000012 [ 3] literal string: @"UIWindow"16@0:8 254 | 0x100002B89 0x00000015 [ 3] literal string: v24@0:8@"UIWindow"16 255 | 0x100002B9E 0x0000000B [ 5] literal string: @24@0:8@16 256 | 0x100002BA9 0x00000044 [ 5] literal string: v40@0:8@"UIScene"16@"UISceneSession"24@"UISceneConnectionOptions"32 257 | 0x100002BED 0x00000014 [ 5] literal string: v24@0:8@"UIScene"16 258 | 0x100002C01 0x0000001E [ 5] literal string: v32@0:8@"UIScene"16@"NSSet"24 259 | 0x100002C1F 0x00000024 [ 5] literal string: @"NSUserActivity"24@0:8@"UIScene"16 260 | 0x100002C43 0x00000021 [ 5] literal string: v32@0:8@"UIScene"16@"NSString"24 261 | 0x100002C64 0x00000027 [ 5] literal string: v32@0:8@"UIScene"16@"NSUserActivity"24 262 | 0x100002C8B 0x0000002D [ 5] literal string: v40@0:8@"UIScene"16@"NSString"24@"NSError"32 263 | 0x100002CB8 0x00000014 [ 5] literal string: v48@0:8@16@24q32@40 264 | 0x100002CCC 0x0000004B [ 5] literal string: v48@0:8@"UIWindowScene"16@""24q32@"UITraitCollection"40 265 | 0x100002D17 0x00000042 [ 5] literal string: v40@0:8@"UIWindowScene"16@"UIApplicationShortcutItem"24@?32 266 | 0x100002D59 0x0000002E [ 5] literal string: v32@0:8@"UIWindowScene"16@"CKShareMetadata"24 267 | 0x100002D87 0x0000000C [ 5] literal string: @"UIWindow" 268 | 0x100002D93 0x00000005 [ 2] literal string: hash 269 | 0x100002D98 0x00000005 [ 2] literal string: TQ,R 270 | 0x100002D9D 0x0000000B [ 2] literal string: superclass 271 | 0x100002DA8 0x00000005 [ 2] literal string: T#,R 272 | 0x100002DAD 0x0000000C [ 2] literal string: description 273 | 0x100002DB9 0x00000011 [ 2] literal string: T@"NSString",R,C 274 | 0x100002DCA 0x00000011 [ 2] literal string: debugDescription 275 | 0x100002DDB 0x00000016 [ 3] literal string: Default Configuration 276 | 0x100002DF1 0x00000007 [ 3] literal string: window 277 | 0x100002DF8 0x00000011 [ 3] literal string: T@"UIWindow",&,N 278 | 0x100002E09 0x0000001A [ 5] literal string: T@"UIWindow",&,N,V_window 279 | 0x100002E23 0x00000170 [ 1] opaque_section 280 | 0x100002F94 0x0000006C [ 0] compact unwind info 281 | 0x100003000 0x00000008 [ 0] non-lazy-pointer-to-local: _objc_msgSend 282 | 0x100003008 0x00000008 [ 0] non-lazy-pointer-to-local: _objc_release 283 | 0x100003010 0x00000008 [ 0] non-lazy-pointer-to-local: _objc_retain 284 | 0x100003018 0x00000008 [ 0] non-lazy-pointer-to-local: dyld_stub_binder 285 | 0x100003020 0x00000020 [ 3] CFString 286 | 0x100003040 0x00000018 [ 2] anon 287 | 0x100003058 0x00000008 [ 3] anon 288 | 0x100003060 0x00000008 [ 5] anon 289 | 0x100003068 0x00000008 [ 2] __OBJC_LABEL_PROTOCOL_$_NSObject 290 | 0x100003070 0x00000008 [ 2] __OBJC_LABEL_PROTOCOL_$_UsedProtocol 291 | 0x100003078 0x00000008 [ 3] __OBJC_LABEL_PROTOCOL_$_UIApplicationDelegate 292 | 0x100003080 0x00000008 [ 5] __OBJC_LABEL_PROTOCOL_$_UISceneDelegate 293 | 0x100003088 0x00000008 [ 5] __OBJC_LABEL_PROTOCOL_$_UIWindowSceneDelegate 294 | 0x100003090 0x00000008 [ 0] objc image info 295 | 0x100004000 0x00000008 [ 6] _NSStringFromClass 296 | 0x100004008 0x00000008 [ 8] _UIApplicationMain 297 | 0x100004010 0x00000008 [ 7] _objc_alloc 298 | 0x100004018 0x00000008 [ 7] _objc_autoreleasePoolPop 299 | 0x100004020 0x00000008 [ 7] _objc_autoreleasePoolPush 300 | 0x100004028 0x00000008 [ 7] _objc_autoreleaseReturnValue 301 | 0x100004030 0x00000008 [ 7] _objc_msgSendSuper2 302 | 0x100004038 0x00000008 [ 7] _objc_opt_class 303 | 0x100004040 0x00000008 [ 7] _objc_opt_new 304 | 0x100004048 0x00000008 [ 7] _objc_retainAutoreleasedReturnValue 305 | 0x100004050 0x00000008 [ 7] _objc_storeStrong 306 | 0x100004058 0x000001D0 [ 2] l_OBJC_$_PROTOCOL_INSTANCE_METHODS_NSObject 307 | 0x100004228 0x00000020 [ 2] l_OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_NSObject 308 | 0x100004248 0x00000048 [ 2] l_OBJC_$_PROP_LIST_NSObject 309 | 0x100004290 0x000000A0 [ 2] l_OBJC_$_PROTOCOL_METHOD_TYPES_NSObject 310 | 0x100004330 0x00000018 [ 2] l_OBJC_$_PROTOCOL_REFS_UsedProtocol 311 | 0x100004348 0x00000020 [ 2] l_OBJC_$_PROTOCOL_INSTANCE_METHODS_UsedProtocol 312 | 0x100004368 0x00000008 [ 2] l_OBJC_$_PROTOCOL_METHOD_TYPES_UsedProtocol 313 | 0x100004370 0x00000018 [ 2] l_OBJC_CLASS_PROTOCOLS_$_UsedClass 314 | 0x100004388 0x00000048 [ 2] l_OBJC_METACLASS_RO_$_UsedClass 315 | 0x1000043D0 0x00000050 [ 2] l_OBJC_$_INSTANCE_METHODS_UsedClass 316 | 0x100004420 0x00000048 [ 2] l_OBJC_$_PROP_LIST_UsedClass 317 | 0x100004468 0x00000048 [ 2] l_OBJC_CLASS_RO_$_UsedClass 318 | 0x1000044B0 0x00000048 [ 2] l_OBJC_METACLASS_RO_$_UnusedClass 319 | 0x1000044F8 0x00000020 [ 2] l_OBJC_$_INSTANCE_METHODS_UnusedClass 320 | 0x100004518 0x00000048 [ 2] l_OBJC_CLASS_RO_$_UnusedClass 321 | 0x100004560 0x00000048 [ 2] l_OBJC_METACLASS_RO_$_ViewController 322 | 0x1000045A8 0x00000020 [ 2] l_OBJC_$_INSTANCE_METHODS_ViewController 323 | 0x1000045C8 0x00000048 [ 2] l_OBJC_CLASS_RO_$_ViewController 324 | 0x100004610 0x000001D0 [ 3] l_OBJC_$_PROTOCOL_INSTANCE_METHODS_NSObject 325 | 0x1000047E0 0x00000020 [ 3] l_OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_NSObject 326 | 0x100004800 0x00000048 [ 3] l_OBJC_$_PROP_LIST_NSObject 327 | 0x100004848 0x000000A0 [ 3] l_OBJC_$_PROTOCOL_METHOD_TYPES_NSObject 328 | 0x1000048E8 0x00000018 [ 3] l_OBJC_$_PROTOCOL_REFS_UIApplicationDelegate 329 | 0x100004900 0x00000500 [ 3] l_OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_UIApplicationDelegate 330 | 0x100004E00 0x00000018 [ 3] l_OBJC_$_PROP_LIST_UIApplicationDelegate 331 | 0x100004E18 0x000001A8 [ 3] l_OBJC_$_PROTOCOL_METHOD_TYPES_UIApplicationDelegate 332 | 0x100004FC0 0x00000018 [ 3] l_OBJC_CLASS_PROTOCOLS_$_AppDelegate 333 | 0x100004FD8 0x00000048 [ 3] l_OBJC_METACLASS_RO_$_AppDelegate 334 | 0x100005020 0x00000050 [ 3] l_OBJC_$_INSTANCE_METHODS_AppDelegate 335 | 0x100005070 0x00000058 [ 3] l_OBJC_$_PROP_LIST_AppDelegate 336 | 0x1000050C8 0x00000048 [ 3] l_OBJC_CLASS_RO_$_AppDelegate 337 | 0x100005110 0x000001D0 [ 5] l_OBJC_$_PROTOCOL_INSTANCE_METHODS_NSObject 338 | 0x1000052E0 0x00000020 [ 5] l_OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_NSObject 339 | 0x100005300 0x00000048 [ 5] l_OBJC_$_PROP_LIST_NSObject 340 | 0x100005348 0x000000A0 [ 5] l_OBJC_$_PROTOCOL_METHOD_TYPES_NSObject 341 | 0x1000053E8 0x00000018 [ 5] l_OBJC_$_PROTOCOL_REFS_UISceneDelegate 342 | 0x100005400 0x00000128 [ 5] l_OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_UISceneDelegate 343 | 0x100005528 0x00000060 [ 5] l_OBJC_$_PROTOCOL_METHOD_TYPES_UISceneDelegate 344 | 0x100005588 0x00000018 [ 5] l_OBJC_$_PROTOCOL_REFS_UIWindowSceneDelegate 345 | 0x1000055A0 0x00000080 [ 5] l_OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_UIWindowSceneDelegate 346 | 0x100005620 0x00000018 [ 5] l_OBJC_$_PROP_LIST_UIWindowSceneDelegate 347 | 0x100005638 0x00000028 [ 5] l_OBJC_$_PROTOCOL_METHOD_TYPES_UIWindowSceneDelegate 348 | 0x100005660 0x00000018 [ 5] l_OBJC_CLASS_PROTOCOLS_$_SceneDelegate 349 | 0x100005678 0x00000048 [ 5] l_OBJC_METACLASS_RO_$_SceneDelegate 350 | 0x1000056C0 0x000000E0 [ 5] l_OBJC_$_INSTANCE_METHODS_SceneDelegate 351 | 0x1000057A0 0x00000028 [ 5] l_OBJC_$_INSTANCE_VARIABLES_SceneDelegate 352 | 0x1000057C8 0x00000058 [ 5] l_OBJC_$_PROP_LIST_SceneDelegate 353 | 0x100005820 0x00000048 [ 5] l_OBJC_CLASS_RO_$_SceneDelegate 354 | 0x100005868 0x00000008 [ 2] pointer-to-literal-cstring 355 | 0x100005870 0x00000008 [ 2] pointer-to-literal-cstring 356 | 0x100005878 0x00000008 [ 3] pointer-to-literal-cstring 357 | 0x100005880 0x00000008 [ 3] pointer-to-literal-cstring 358 | 0x100005888 0x00000008 [ 2] objc-class-ref 359 | 0x100005890 0x00000008 [ 3] objc-class-ref 360 | 0x100005898 0x00000008 [ 4] objc-class-ref 361 | 0x1000058A0 0x00000008 [ 2] anon 362 | 0x1000058A8 0x00000008 [ 5] _OBJC_IVAR_$_SceneDelegate._window 363 | 0x1000058B0 0x00000028 [ 2] _OBJC_METACLASS_$_UsedClass 364 | 0x1000058D8 0x00000028 [ 2] _OBJC_CLASS_$_UsedClass 365 | 0x100005900 0x00000028 [ 2] _OBJC_METACLASS_$_UnusedClass 366 | 0x100005928 0x00000028 [ 2] _OBJC_CLASS_$_UnusedClass 367 | 0x100005950 0x00000028 [ 2] _OBJC_CLASS_$_ViewController 368 | 0x100005978 0x00000028 [ 2] _OBJC_METACLASS_$_ViewController 369 | 0x1000059A0 0x00000028 [ 3] _OBJC_METACLASS_$_AppDelegate 370 | 0x1000059C8 0x00000028 [ 3] _OBJC_CLASS_$_AppDelegate 371 | 0x1000059F0 0x00000028 [ 5] _OBJC_METACLASS_$_SceneDelegate 372 | 0x100005A18 0x00000028 [ 5] _OBJC_CLASS_$_SceneDelegate 373 | 0x100005A40 0x00000008 [ 0] __dyld_private 374 | 0x100005A48 0x00000060 [ 2] __OBJC_PROTOCOL_$_NSObject 375 | 0x100005AA8 0x00000060 [ 2] __OBJC_PROTOCOL_$_UsedProtocol 376 | 0x100005B08 0x00000060 [ 3] __OBJC_PROTOCOL_$_UIApplicationDelegate 377 | 0x100005B68 0x00000060 [ 5] __OBJC_PROTOCOL_$_UISceneDelegate 378 | 0x100005BC8 0x00000060 [ 5] __OBJC_PROTOCOL_$_UIWindowSceneDelegate 379 | 380 | # Dead Stripped Symbols: 381 | # Size File Name 382 | <> 0x00000018 [ 2] CIE 383 | <> 0x00000005 [ 3] literal string: hash 384 | <> 0x00000005 [ 3] literal string: TQ,R 385 | <> 0x0000000B [ 3] literal string: superclass 386 | <> 0x00000005 [ 3] literal string: T#,R 387 | <> 0x0000000C [ 3] literal string: description 388 | <> 0x00000011 [ 3] literal string: T@"NSString",R,C 389 | <> 0x00000011 [ 3] literal string: debugDescription 390 | <> 0x00000009 [ 3] literal string: isEqual: 391 | <> 0x00000006 [ 3] literal string: class 392 | <> 0x00000005 [ 3] literal string: self 393 | <> 0x00000011 [ 3] literal string: performSelector: 394 | <> 0x0000001C [ 3] literal string: performSelector:withObject: 395 | <> 0x00000027 [ 3] literal string: performSelector:withObject:withObject: 396 | <> 0x00000008 [ 3] literal string: isProxy 397 | <> 0x0000000F [ 3] literal string: isKindOfClass: 398 | <> 0x00000011 [ 3] literal string: isMemberOfClass: 399 | <> 0x00000014 [ 3] literal string: conformsToProtocol: 400 | <> 0x00000014 [ 3] literal string: respondsToSelector: 401 | <> 0x00000007 [ 3] literal string: retain 402 | <> 0x00000008 [ 3] literal string: release 403 | <> 0x0000000C [ 3] literal string: autorelease 404 | <> 0x0000000C [ 3] literal string: retainCount 405 | <> 0x00000005 [ 3] literal string: zone 406 | <> 0x00000005 [ 3] literal string: hash 407 | <> 0x0000000B [ 3] literal string: superclass 408 | <> 0x0000000C [ 3] literal string: description 409 | <> 0x00000011 [ 3] literal string: debugDescription 410 | <> 0x00000009 [ 3] literal string: NSObject 411 | <> 0x0000000B [ 3] literal string: B24@0:8@16 412 | <> 0x00000008 [ 3] literal string: #16@0:8 413 | <> 0x00000008 [ 3] literal string: @16@0:8 414 | <> 0x0000000B [ 3] literal string: @24@0:8:16 415 | <> 0x0000000E [ 3] literal string: @32@0:8:16@24 416 | <> 0x00000011 [ 3] literal string: @40@0:8:16@24@32 417 | <> 0x00000008 [ 3] literal string: B16@0:8 418 | <> 0x0000000B [ 3] literal string: B24@0:8#16 419 | <> 0x0000000B [ 3] literal string: B24@0:8:16 420 | <> 0x00000009 [ 3] literal string: Vv16@0:8 421 | <> 0x00000008 [ 3] literal string: Q16@0:8 422 | <> 0x00000012 [ 3] literal string: ^{_NSZone=}16@0:8 423 | <> 0x00000015 [ 3] literal string: B24@0:8@"Protocol"16 424 | <> 0x00000012 [ 3] literal string: @"NSString"16@0:8 425 | <> 0x00000018 [ 3] CIE 426 | <> 0x00000018 [ 4] CIE 427 | <> 0x00000009 [ 5] literal string: NSObject 428 | <> 0x00000009 [ 5] literal string: isEqual: 429 | <> 0x00000006 [ 5] literal string: class 430 | <> 0x00000005 [ 5] literal string: self 431 | <> 0x00000011 [ 5] literal string: performSelector: 432 | <> 0x0000001C [ 5] literal string: performSelector:withObject: 433 | <> 0x00000027 [ 5] literal string: performSelector:withObject:withObject: 434 | <> 0x00000008 [ 5] literal string: isProxy 435 | <> 0x0000000F [ 5] literal string: isKindOfClass: 436 | <> 0x00000011 [ 5] literal string: isMemberOfClass: 437 | <> 0x00000014 [ 5] literal string: conformsToProtocol: 438 | <> 0x00000014 [ 5] literal string: respondsToSelector: 439 | <> 0x00000007 [ 5] literal string: retain 440 | <> 0x00000008 [ 5] literal string: release 441 | <> 0x0000000C [ 5] literal string: autorelease 442 | <> 0x0000000C [ 5] literal string: retainCount 443 | <> 0x00000005 [ 5] literal string: zone 444 | <> 0x00000005 [ 5] literal string: hash 445 | <> 0x0000000B [ 5] literal string: superclass 446 | <> 0x0000000C [ 5] literal string: description 447 | <> 0x00000011 [ 5] literal string: debugDescription 448 | <> 0x00000007 [ 5] literal string: window 449 | <> 0x0000000B [ 5] literal string: setWindow: 450 | <> 0x0000000B [ 5] literal string: B24@0:8@16 451 | <> 0x00000008 [ 5] literal string: #16@0:8 452 | <> 0x00000008 [ 5] literal string: @16@0:8 453 | <> 0x0000000B [ 5] literal string: @24@0:8:16 454 | <> 0x0000000E [ 5] literal string: @32@0:8:16@24 455 | <> 0x00000011 [ 5] literal string: @40@0:8:16@24@32 456 | <> 0x00000008 [ 5] literal string: B16@0:8 457 | <> 0x0000000B [ 5] literal string: B24@0:8#16 458 | <> 0x0000000B [ 5] literal string: B24@0:8:16 459 | <> 0x00000009 [ 5] literal string: Vv16@0:8 460 | <> 0x00000008 [ 5] literal string: Q16@0:8 461 | <> 0x00000012 [ 5] literal string: ^{_NSZone=}16@0:8 462 | <> 0x00000015 [ 5] literal string: B24@0:8@"Protocol"16 463 | <> 0x00000012 [ 5] literal string: @"NSString"16@0:8 464 | <> 0x00000011 [ 5] literal string: v40@0:8@16@24@32 465 | <> 0x0000000B [ 5] literal string: v24@0:8@16 466 | <> 0x0000000E [ 5] literal string: v32@0:8@16@24 467 | <> 0x00000012 [ 5] literal string: v40@0:8@16@24@?32 468 | <> 0x00000012 [ 5] literal string: @"UIWindow"16@0:8 469 | <> 0x00000015 [ 5] literal string: v24@0:8@"UIWindow"16 470 | <> 0x00000008 [ 5] literal string: v16@0:8 471 | <> 0x00000005 [ 5] literal string: hash 472 | <> 0x00000005 [ 5] literal string: TQ,R 473 | <> 0x0000000B [ 5] literal string: superclass 474 | <> 0x00000005 [ 5] literal string: T#,R 475 | <> 0x0000000C [ 5] literal string: description 476 | <> 0x00000011 [ 5] literal string: T@"NSString",R,C 477 | <> 0x00000011 [ 5] literal string: debugDescription 478 | <> 0x00000007 [ 5] literal string: window 479 | <> 0x00000011 [ 5] literal string: T@"UIWindow",&,N 480 | <> 0x00000018 [ 5] CIE 481 | -------------------------------------------------------------------------------- /demo/release/demo.app/Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flexih/Snake/cb6f7ac3ebbf4bdf963ff6800f5716b3387bfbd0/demo/release/demo.app/Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib -------------------------------------------------------------------------------- /demo/release/demo.app/Base.lproj/LaunchScreen.storyboardc/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flexih/Snake/cb6f7ac3ebbf4bdf963ff6800f5716b3387bfbd0/demo/release/demo.app/Base.lproj/LaunchScreen.storyboardc/Info.plist -------------------------------------------------------------------------------- /demo/release/demo.app/Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flexih/Snake/cb6f7ac3ebbf4bdf963ff6800f5716b3387bfbd0/demo/release/demo.app/Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib -------------------------------------------------------------------------------- /demo/release/demo.app/Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flexih/Snake/cb6f7ac3ebbf4bdf963ff6800f5716b3387bfbd0/demo/release/demo.app/Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib -------------------------------------------------------------------------------- /demo/release/demo.app/Base.lproj/Main.storyboardc/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flexih/Snake/cb6f7ac3ebbf4bdf963ff6800f5716b3387bfbd0/demo/release/demo.app/Base.lproj/Main.storyboardc/Info.plist -------------------------------------------------------------------------------- /demo/release/demo.app/Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flexih/Snake/cb6f7ac3ebbf4bdf963ff6800f5716b3387bfbd0/demo/release/demo.app/Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib -------------------------------------------------------------------------------- /demo/release/demo.app/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flexih/Snake/cb6f7ac3ebbf4bdf963ff6800f5716b3387bfbd0/demo/release/demo.app/Info.plist -------------------------------------------------------------------------------- /demo/release/demo.app/PkgInfo: -------------------------------------------------------------------------------- 1 | APPL???? -------------------------------------------------------------------------------- /demo/release/demo.app/_CodeSignature/CodeResources: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | files 6 | 7 | Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib 8 | 9 | Yb08F0QKbe/Bgp8010ncoy0+rX0= 10 | 11 | Base.lproj/LaunchScreen.storyboardc/Info.plist 12 | 13 | n2t8gsDpfE6XkhG31p7IQJRxTxU= 14 | 15 | Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib 16 | 17 | 8Hqn+O3es6PvUVS1UeXPg8yCK6Q= 18 | 19 | Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib 20 | 21 | CzX1nCQE7EWD7qN5de/dVXTtH9o= 22 | 23 | Base.lproj/Main.storyboardc/Info.plist 24 | 25 | MDrKFvFWroTb0+KEbQShBcoBvo4= 26 | 27 | Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib 28 | 29 | yBHVeVpBKSQ9pLtAeNiES6K0QX0= 30 | 31 | Info.plist 32 | 33 | fHI7/JoE//RzBfo2tuHUhlvCXBo= 34 | 35 | PkgInfo 36 | 37 | n57qDP4tZfLD1rCS43W0B4LQjzE= 38 | 39 | 40 | files2 41 | 42 | Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib 43 | 44 | hash2 45 | 46 | tsGiXqHCQVFWAg0nchUwCCfZFyDJ5sGtkBCcyT6U5cM= 47 | 48 | 49 | Base.lproj/LaunchScreen.storyboardc/Info.plist 50 | 51 | hash2 52 | 53 | HyVdXMU7Ux4/KalAao30mpWOK/lEPT4gvYN09wf31cg= 54 | 55 | 56 | Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib 57 | 58 | hash2 59 | 60 | gT8x1PutctwwXfWqll/b9JwdEmm2LHtHY2ixI7xgdcA= 61 | 62 | 63 | Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib 64 | 65 | hash2 66 | 67 | zgoPS9Luw3TcUOPQL+QdyltmXg82NVzqC9gORqtK+zA= 68 | 69 | 70 | Base.lproj/Main.storyboardc/Info.plist 71 | 72 | hash2 73 | 74 | PpvapAjR62rl6Ym4E6hkTgpKmBICxTaQXeUqcpHmmqQ= 75 | 76 | 77 | Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib 78 | 79 | hash2 80 | 81 | iH1wTmCXvP/pEwvQqRFbyCq6V9Uz9lggerffHtsHZEw= 82 | 83 | 84 | 85 | rules 86 | 87 | ^.* 88 | 89 | ^.*\.lproj/ 90 | 91 | optional 92 | 93 | weight 94 | 1000 95 | 96 | ^.*\.lproj/locversion.plist$ 97 | 98 | omit 99 | 100 | weight 101 | 1100 102 | 103 | ^Base\.lproj/ 104 | 105 | weight 106 | 1010 107 | 108 | ^version.plist$ 109 | 110 | 111 | rules2 112 | 113 | .*\.dSYM($|/) 114 | 115 | weight 116 | 11 117 | 118 | ^(.*/)?\.DS_Store$ 119 | 120 | omit 121 | 122 | weight 123 | 2000 124 | 125 | ^.* 126 | 127 | ^.*\.lproj/ 128 | 129 | optional 130 | 131 | weight 132 | 1000 133 | 134 | ^.*\.lproj/locversion.plist$ 135 | 136 | omit 137 | 138 | weight 139 | 1100 140 | 141 | ^Base\.lproj/ 142 | 143 | weight 144 | 1010 145 | 146 | ^Info\.plist$ 147 | 148 | omit 149 | 150 | weight 151 | 20 152 | 153 | ^PkgInfo$ 154 | 155 | omit 156 | 157 | weight 158 | 20 159 | 160 | ^embedded\.provisionprofile$ 161 | 162 | weight 163 | 20 164 | 165 | ^version\.plist$ 166 | 167 | weight 168 | 20 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /demo/release/demo.app/demo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flexih/Snake/cb6f7ac3ebbf4bdf963ff6800f5716b3387bfbd0/demo/release/demo.app/demo -------------------------------------------------------------------------------- /snake.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 713CADA623D4433B0069C5A0 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 713CADA523D4433B0069C5A0 /* main.cpp */; }; 11 | 713CADAF23D5A71D0069C5A0 /* Arch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 713CADAE23D5A71D0069C5A0 /* Arch.cpp */; }; 12 | 713CADB223D5A7710069C5A0 /* Bin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 713CADB123D5A7710069C5A0 /* Bin.cpp */; }; 13 | 71CB46B323E309F40047F3B2 /* utility.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71CB46B123E309F40047F3B2 /* utility.cpp */; }; 14 | 71FE52B423D83C390043D103 /* Output.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71FE52B223D83C390043D103 /* Output.cpp */; }; 15 | 71FE52B823D8542F0043D103 /* Linkmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71FE52B623D8542F0043D103 /* Linkmap.cpp */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | 713CADA023D4433B0069C5A0 /* CopyFiles */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = /usr/share/man/man1/; 23 | dstSubfolderSpec = 0; 24 | files = ( 25 | ); 26 | runOnlyForDeploymentPostprocessing = 1; 27 | }; 28 | /* End PBXCopyFilesBuildPhase section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 713CADA223D4433B0069C5A0 /* snake */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = snake; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | 713CADA523D4433B0069C5A0 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 33 | 713CADAC23D45E020069C5A0 /* ObjCRuntime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ObjCRuntime.h; sourceTree = ""; }; 34 | 713CADAD23D5A71D0069C5A0 /* Arch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Arch.h; sourceTree = ""; }; 35 | 713CADAE23D5A71D0069C5A0 /* Arch.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Arch.cpp; sourceTree = ""; }; 36 | 713CADB023D5A7710069C5A0 /* Bin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Bin.h; sourceTree = ""; }; 37 | 713CADB123D5A7710069C5A0 /* Bin.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Bin.cpp; sourceTree = ""; }; 38 | 713CADB323D5B32B0069C5A0 /* ObjCClass.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ObjCClass.hpp; sourceTree = ""; }; 39 | 71848CCF23E54E9C00E55D7E /* cxxopts.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cxxopts.hpp; sourceTree = ""; }; 40 | 71CB46B123E309F40047F3B2 /* utility.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = utility.cpp; sourceTree = ""; }; 41 | 71CB46B223E309F40047F3B2 /* utility.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = utility.hpp; sourceTree = ""; }; 42 | 71FE52B223D83C390043D103 /* Output.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Output.cpp; sourceTree = ""; }; 43 | 71FE52B323D83C390043D103 /* Output.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Output.hpp; sourceTree = ""; }; 44 | 71FE52B623D8542F0043D103 /* Linkmap.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Linkmap.cpp; sourceTree = ""; }; 45 | 71FE52B723D8542F0043D103 /* Linkmap.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Linkmap.hpp; sourceTree = ""; }; 46 | /* End PBXFileReference section */ 47 | 48 | /* Begin PBXFrameworksBuildPhase section */ 49 | 713CAD9F23D4433B0069C5A0 /* Frameworks */ = { 50 | isa = PBXFrameworksBuildPhase; 51 | buildActionMask = 2147483647; 52 | files = ( 53 | ); 54 | runOnlyForDeploymentPostprocessing = 0; 55 | }; 56 | /* End PBXFrameworksBuildPhase section */ 57 | 58 | /* Begin PBXGroup section */ 59 | 7129659023D7118A00D59718 /* objc */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | 713CADAC23D45E020069C5A0 /* ObjCRuntime.h */, 63 | 713CADB323D5B32B0069C5A0 /* ObjCClass.hpp */, 64 | ); 65 | path = objc; 66 | sourceTree = ""; 67 | }; 68 | 7129659123D711AA00D59718 /* mach-o */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 713CADB023D5A7710069C5A0 /* Bin.h */, 72 | 713CADB123D5A7710069C5A0 /* Bin.cpp */, 73 | 713CADAD23D5A71D0069C5A0 /* Arch.h */, 74 | 713CADAE23D5A71D0069C5A0 /* Arch.cpp */, 75 | ); 76 | path = "mach-o"; 77 | sourceTree = ""; 78 | }; 79 | 713CAD9923D4433B0069C5A0 = { 80 | isa = PBXGroup; 81 | children = ( 82 | 713CADA423D4433B0069C5A0 /* snake */, 83 | 713CADA323D4433B0069C5A0 /* Products */, 84 | ); 85 | sourceTree = ""; 86 | }; 87 | 713CADA323D4433B0069C5A0 /* Products */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 713CADA223D4433B0069C5A0 /* snake */, 91 | ); 92 | name = Products; 93 | sourceTree = ""; 94 | }; 95 | 713CADA423D4433B0069C5A0 /* snake */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 71848CBC23E54E9C00E55D7E /* vendor */, 99 | 71CB46B023E309C90047F3B2 /* algorithm */, 100 | 71FE52B523D853E90043D103 /* linkmap */, 101 | 71FE52AE23D83BFD0043D103 /* output */, 102 | 7129659123D711AA00D59718 /* mach-o */, 103 | 7129659023D7118A00D59718 /* objc */, 104 | 713CADA523D4433B0069C5A0 /* main.cpp */, 105 | ); 106 | path = snake; 107 | sourceTree = ""; 108 | }; 109 | 71848CBC23E54E9C00E55D7E /* vendor */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 71848CBD23E54E9C00E55D7E /* cxxopts */, 113 | ); 114 | path = vendor; 115 | sourceTree = SOURCE_ROOT; 116 | }; 117 | 71848CBD23E54E9C00E55D7E /* cxxopts */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 71848CCE23E54E9C00E55D7E /* include */, 121 | ); 122 | path = cxxopts; 123 | sourceTree = ""; 124 | }; 125 | 71848CCE23E54E9C00E55D7E /* include */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 71848CCF23E54E9C00E55D7E /* cxxopts.hpp */, 129 | ); 130 | path = include; 131 | sourceTree = ""; 132 | }; 133 | 71CB46B023E309C90047F3B2 /* algorithm */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 71CB46B123E309F40047F3B2 /* utility.cpp */, 137 | 71CB46B223E309F40047F3B2 /* utility.hpp */, 138 | ); 139 | path = algorithm; 140 | sourceTree = ""; 141 | }; 142 | 71FE52AE23D83BFD0043D103 /* output */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 71FE52B323D83C390043D103 /* Output.hpp */, 146 | 71FE52B223D83C390043D103 /* Output.cpp */, 147 | ); 148 | path = output; 149 | sourceTree = ""; 150 | }; 151 | 71FE52B523D853E90043D103 /* linkmap */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 71FE52B623D8542F0043D103 /* Linkmap.cpp */, 155 | 71FE52B723D8542F0043D103 /* Linkmap.hpp */, 156 | ); 157 | path = linkmap; 158 | sourceTree = ""; 159 | }; 160 | /* End PBXGroup section */ 161 | 162 | /* Begin PBXNativeTarget section */ 163 | 713CADA123D4433B0069C5A0 /* snake */ = { 164 | isa = PBXNativeTarget; 165 | buildConfigurationList = 713CADA923D4433B0069C5A0 /* Build configuration list for PBXNativeTarget "snake" */; 166 | buildPhases = ( 167 | 713CAD9E23D4433B0069C5A0 /* Sources */, 168 | 713CAD9F23D4433B0069C5A0 /* Frameworks */, 169 | 713CADA023D4433B0069C5A0 /* CopyFiles */, 170 | ); 171 | buildRules = ( 172 | ); 173 | dependencies = ( 174 | ); 175 | name = snake; 176 | productName = snake; 177 | productReference = 713CADA223D4433B0069C5A0 /* snake */; 178 | productType = "com.apple.product-type.tool"; 179 | }; 180 | /* End PBXNativeTarget section */ 181 | 182 | /* Begin PBXProject section */ 183 | 713CAD9A23D4433B0069C5A0 /* Project object */ = { 184 | isa = PBXProject; 185 | attributes = { 186 | LastUpgradeCheck = 1130; 187 | ORGANIZATIONNAME = flexih; 188 | TargetAttributes = { 189 | 713CADA123D4433B0069C5A0 = { 190 | CreatedOnToolsVersion = 11.3.1; 191 | }; 192 | }; 193 | }; 194 | buildConfigurationList = 713CAD9D23D4433B0069C5A0 /* Build configuration list for PBXProject "snake" */; 195 | compatibilityVersion = "Xcode 9.3"; 196 | developmentRegion = en; 197 | hasScannedForEncodings = 0; 198 | knownRegions = ( 199 | en, 200 | Base, 201 | ); 202 | mainGroup = 713CAD9923D4433B0069C5A0; 203 | productRefGroup = 713CADA323D4433B0069C5A0 /* Products */; 204 | projectDirPath = ""; 205 | projectRoot = ""; 206 | targets = ( 207 | 713CADA123D4433B0069C5A0 /* snake */, 208 | ); 209 | }; 210 | /* End PBXProject section */ 211 | 212 | /* Begin PBXSourcesBuildPhase section */ 213 | 713CAD9E23D4433B0069C5A0 /* Sources */ = { 214 | isa = PBXSourcesBuildPhase; 215 | buildActionMask = 2147483647; 216 | files = ( 217 | 71CB46B323E309F40047F3B2 /* utility.cpp in Sources */, 218 | 713CADB223D5A7710069C5A0 /* Bin.cpp in Sources */, 219 | 713CADA623D4433B0069C5A0 /* main.cpp in Sources */, 220 | 71FE52B423D83C390043D103 /* Output.cpp in Sources */, 221 | 713CADAF23D5A71D0069C5A0 /* Arch.cpp in Sources */, 222 | 71FE52B823D8542F0043D103 /* Linkmap.cpp in Sources */, 223 | ); 224 | runOnlyForDeploymentPostprocessing = 0; 225 | }; 226 | /* End PBXSourcesBuildPhase section */ 227 | 228 | /* Begin XCBuildConfiguration section */ 229 | 713CADA723D4433B0069C5A0 /* Debug */ = { 230 | isa = XCBuildConfiguration; 231 | buildSettings = { 232 | ALWAYS_SEARCH_USER_PATHS = NO; 233 | CLANG_ANALYZER_NONNULL = YES; 234 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 235 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 236 | CLANG_CXX_LIBRARY = "libc++"; 237 | CLANG_ENABLE_MODULES = YES; 238 | CLANG_ENABLE_OBJC_ARC = YES; 239 | CLANG_ENABLE_OBJC_WEAK = YES; 240 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 241 | CLANG_WARN_BOOL_CONVERSION = YES; 242 | CLANG_WARN_COMMA = YES; 243 | CLANG_WARN_CONSTANT_CONVERSION = YES; 244 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 245 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 246 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 247 | CLANG_WARN_EMPTY_BODY = YES; 248 | CLANG_WARN_ENUM_CONVERSION = YES; 249 | CLANG_WARN_INFINITE_RECURSION = YES; 250 | CLANG_WARN_INT_CONVERSION = YES; 251 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 252 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 253 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 255 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 256 | CLANG_WARN_STRICT_PROTOTYPES = YES; 257 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 258 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 259 | CLANG_WARN_UNREACHABLE_CODE = YES; 260 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 261 | COPY_PHASE_STRIP = NO; 262 | DEBUG_INFORMATION_FORMAT = dwarf; 263 | ENABLE_STRICT_OBJC_MSGSEND = YES; 264 | ENABLE_TESTABILITY = YES; 265 | GCC_C_LANGUAGE_STANDARD = gnu11; 266 | GCC_DYNAMIC_NO_PIC = NO; 267 | GCC_NO_COMMON_BLOCKS = YES; 268 | GCC_OPTIMIZATION_LEVEL = 0; 269 | GCC_PREPROCESSOR_DEFINITIONS = ( 270 | "DEBUG=1", 271 | "$(inherited)", 272 | ); 273 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 274 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 275 | GCC_WARN_UNDECLARED_SELECTOR = YES; 276 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 277 | GCC_WARN_UNUSED_FUNCTION = YES; 278 | GCC_WARN_UNUSED_VARIABLE = YES; 279 | MACOSX_DEPLOYMENT_TARGET = 10.15; 280 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 281 | MTL_FAST_MATH = YES; 282 | ONLY_ACTIVE_ARCH = YES; 283 | SDKROOT = macosx; 284 | }; 285 | name = Debug; 286 | }; 287 | 713CADA823D4433B0069C5A0 /* Release */ = { 288 | isa = XCBuildConfiguration; 289 | buildSettings = { 290 | ALWAYS_SEARCH_USER_PATHS = NO; 291 | CLANG_ANALYZER_NONNULL = YES; 292 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 293 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 294 | CLANG_CXX_LIBRARY = "libc++"; 295 | CLANG_ENABLE_MODULES = YES; 296 | CLANG_ENABLE_OBJC_ARC = YES; 297 | CLANG_ENABLE_OBJC_WEAK = YES; 298 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 299 | CLANG_WARN_BOOL_CONVERSION = YES; 300 | CLANG_WARN_COMMA = YES; 301 | CLANG_WARN_CONSTANT_CONVERSION = YES; 302 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 303 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 304 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 305 | CLANG_WARN_EMPTY_BODY = YES; 306 | CLANG_WARN_ENUM_CONVERSION = YES; 307 | CLANG_WARN_INFINITE_RECURSION = YES; 308 | CLANG_WARN_INT_CONVERSION = YES; 309 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 310 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 311 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 312 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 313 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 314 | CLANG_WARN_STRICT_PROTOTYPES = YES; 315 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 316 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 317 | CLANG_WARN_UNREACHABLE_CODE = YES; 318 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 319 | COPY_PHASE_STRIP = NO; 320 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 321 | ENABLE_NS_ASSERTIONS = NO; 322 | ENABLE_STRICT_OBJC_MSGSEND = YES; 323 | GCC_C_LANGUAGE_STANDARD = gnu11; 324 | GCC_NO_COMMON_BLOCKS = YES; 325 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 326 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 327 | GCC_WARN_UNDECLARED_SELECTOR = YES; 328 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 329 | GCC_WARN_UNUSED_FUNCTION = YES; 330 | GCC_WARN_UNUSED_VARIABLE = YES; 331 | MACOSX_DEPLOYMENT_TARGET = 10.15; 332 | MTL_ENABLE_DEBUG_INFO = NO; 333 | MTL_FAST_MATH = YES; 334 | SDKROOT = macosx; 335 | }; 336 | name = Release; 337 | }; 338 | 713CADAA23D4433B0069C5A0 /* Debug */ = { 339 | isa = XCBuildConfiguration; 340 | buildSettings = { 341 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 342 | CODE_SIGN_STYLE = Automatic; 343 | PRODUCT_NAME = "$(TARGET_NAME)"; 344 | }; 345 | name = Debug; 346 | }; 347 | 713CADAB23D4433B0069C5A0 /* Release */ = { 348 | isa = XCBuildConfiguration; 349 | buildSettings = { 350 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 351 | CODE_SIGN_STYLE = Automatic; 352 | PRODUCT_NAME = "$(TARGET_NAME)"; 353 | }; 354 | name = Release; 355 | }; 356 | /* End XCBuildConfiguration section */ 357 | 358 | /* Begin XCConfigurationList section */ 359 | 713CAD9D23D4433B0069C5A0 /* Build configuration list for PBXProject "snake" */ = { 360 | isa = XCConfigurationList; 361 | buildConfigurations = ( 362 | 713CADA723D4433B0069C5A0 /* Debug */, 363 | 713CADA823D4433B0069C5A0 /* Release */, 364 | ); 365 | defaultConfigurationIsVisible = 0; 366 | defaultConfigurationName = Release; 367 | }; 368 | 713CADA923D4433B0069C5A0 /* Build configuration list for PBXNativeTarget "snake" */ = { 369 | isa = XCConfigurationList; 370 | buildConfigurations = ( 371 | 713CADAA23D4433B0069C5A0 /* Debug */, 372 | 713CADAB23D4433B0069C5A0 /* Release */, 373 | ); 374 | defaultConfigurationIsVisible = 0; 375 | defaultConfigurationName = Release; 376 | }; 377 | /* End XCConfigurationList section */ 378 | }; 379 | rootObject = 713CAD9A23D4433B0069C5A0 /* Project object */; 380 | } 381 | -------------------------------------------------------------------------------- /snake.xcodeproj/xcshareddata/xcschemes/snake.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /snake/algorithm/utility.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // utility.cpp 3 | // snake 4 | // 5 | // Created by flexih on 2020/1/30. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #include "utility.hpp" 10 | 11 | namespace snake { 12 | std::string trimPath(const std::string &path) { 13 | std::string result; 14 | for (auto &c : path) { 15 | if (c != '\\') { 16 | result.append(1, c); 17 | } 18 | } 19 | return result; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /snake/algorithm/utility.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // utility.hpp 3 | // snake 4 | // 5 | // Created by flexih on 2020/1/30. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #ifndef utility_hpp 10 | #define utility_hpp 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | namespace snake { 17 | template 18 | void discard_if(std::set& c, Predicate pred) { 19 | for (auto it{c.begin()}, end{c.end()}; it != end; ) { 20 | if (pred(*it)) { 21 | it = c.erase(it); 22 | } 23 | else { 24 | ++it; 25 | } 26 | } 27 | } 28 | 29 | template 30 | void discard_if(std::map& c, Predicate pred) { 31 | for (auto it{c.begin()}, end{c.end()}; it != end; ) { 32 | if (pred(*it)) { 33 | it = c.erase(it); 34 | } 35 | else { 36 | ++it; 37 | } 38 | } 39 | } 40 | 41 | template 42 | auto contains(std::set& c, T k) { 43 | return c.find(k) != c.end(); 44 | } 45 | 46 | template 47 | auto contains(std::map& c, Key k) { 48 | return c.find(k) != c.end(); 49 | } 50 | 51 | std::string trimPath(const std::string &path); 52 | } 53 | 54 | #endif /* utility_hpp */ 55 | -------------------------------------------------------------------------------- /snake/linkmap/Linkmap.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // linkmap.cpp 3 | // snake 4 | // 5 | // Created by flexih on 2020/1/22. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #include "Linkmap.hpp" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "utility.hpp" 17 | 18 | namespace snake { 19 | Linkmap::~Linkmap() { 20 | if (pile) { 21 | munmap(pile, size); 22 | } 23 | } 24 | bool Linkmap::read(std::string &path) { 25 | if (path.empty()) return false; 26 | if (pile) { 27 | munmap(pile, size); 28 | pile = nullptr; 29 | } 30 | int fd = open(path.c_str(), O_RDONLY, S_IRUSR | S_IWUSR); 31 | if (fd < 0) { 32 | return false; 33 | } 34 | struct stat st; 35 | fstat(fd, &st); 36 | pile = mmap(nullptr, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); 37 | close(fd); 38 | size = st.st_size; 39 | return parse(); 40 | } 41 | enum { 42 | Op = 0, 43 | OpObjectFiles = 3, 44 | OpSymbols = 5 45 | }; 46 | bool Linkmap::parse() { 47 | const char *Path = "Path"; 48 | const char *ObjectFiles = "Object files"; 49 | const char *Symbols = "Symbols"; 50 | const auto FILELENGTH = 4096; 51 | 52 | int state = Op; 53 | char buff[FILELENGTH]; 54 | auto p = (char *)pile; 55 | auto end = p + size; 56 | char *n = 0; 57 | while (p < end && (n = (char *)memchr(p, '\n', end - p)) != nullptr) { 58 | n[0] = 0; 59 | if (p[0] == '#') { 60 | if (strchr(p, ':') != nullptr) { 61 | if (sscanf(p, "#%*[ ]%4095[a-zA-Z ]", buff) != 1) { 62 | return false; 63 | } 64 | if (strcmp(Path, buff) == 0) { 65 | if (auto lastPathComponent = strrchr(p, '/'); lastPathComponent && lastPathComponent + 1 < n) { 66 | archName = std::string(lastPathComponent + 1, n - lastPathComponent - 1); 67 | } 68 | } else if (strcmp(ObjectFiles, buff) == 0) { 69 | if (state >= OpObjectFiles) { 70 | return false; 71 | } 72 | state = OpObjectFiles; 73 | } else if (strcmp(Symbols, buff) == 0) { 74 | if (state >= OpSymbols) { 75 | return false; 76 | } 77 | state = OpSymbols; 78 | } else { 79 | state += 1; 80 | if (state == OpSymbols) { 81 | return false; 82 | } else if (state > OpSymbols) { 83 | return true; 84 | } 85 | } 86 | } 87 | } else if (n > p) { 88 | if (state == OpObjectFiles) { 89 | int index = 0; 90 | if (sscanf(p, "[%d]%*[ ]%4095[^\n]", &index, buff) != 2) { 91 | return false; 92 | } 93 | std::string compoment(buff); 94 | size_t i = compoment.rfind('/', std::string::npos); 95 | std::string last; 96 | auto useParentDirName = false; 97 | if (i != std::string::npos && i + 1 < compoment.size()) { 98 | last = compoment.substr(i + 1); 99 | //xxx.a(xxx.o) 100 | //xxx.app-Simulated.xcent 101 | //xxx.o 102 | //lib/libc.tbd 103 | //Foundation.framework/Foundation.tbd 104 | //xxx.framework/xxx(xxx.o) 105 | if (last.back() == ')') { 106 | auto j = last.rfind('(', std::string::npos); 107 | if (j == std::string::npos) { 108 | return false; 109 | } 110 | if (auto t = last.rfind(".a", j); t != std::string::npos && t + 2 == j) { 111 | addLibName(index, last.substr(0, j)); 112 | } else { 113 | useParentDirName = true; 114 | } 115 | } else { 116 | auto j = last.rfind('.', std::string::npos); 117 | if (j == std::string::npos) { 118 | useParentDirName = true; 119 | } else if (last.compare(j + 1, last.size() - j - 1, "o") == 0) { 120 | addLibName(index, archName); 121 | } else if (last.compare(j + 1, last.size() - j - 1, "tbd") == 0) { 122 | useParentDirName = true; 123 | } else { 124 | addLibName(index, last); 125 | } 126 | } 127 | } else { 128 | addLibName(index, compoment); 129 | } 130 | if (useParentDirName) { 131 | auto j = compoment.rfind('/', i - 1); 132 | if (j == std::string::npos) { 133 | return false; 134 | } 135 | auto dirName = compoment.substr(j + 1, i - j - 1); 136 | if (dirName.find('.') != std::string::npos) { 137 | addLibName(index, dirName); 138 | } else { 139 | addLibName(index, last); 140 | } 141 | } 142 | } else if (state == OpSymbols) { 143 | int8_t op; 144 | size_t index; 145 | size_t sz; 146 | if (n[-1] == ']' && sscanf(p, "%*[0-9a-zA-Z]%*[ \t]%zx%*[ \t][%zu]%*[ \t]%[+-]", &sz, &index, &op) == 3) { 147 | if (auto pmeth = strchr(p, op); auto split = strchr(pmeth + 1, ' ')) { 148 | insert(index, pmeth + 2, split - pmeth - 2, pmeth, n - pmeth, sz); 149 | } 150 | } else if (sscanf(p, "%*[0-9a-zA-Z]%*[ \t]%zx%*[ \t][%zu]%*[ \t]%*[l_]OBJC_PROTOCOL_$_%4095s", &sz, &index, buff) == 3) { 151 | std::string protocolName(buff); 152 | if (!contains(protocols, protocolName)) { 153 | protocols[std::move(protocolName)] = indexs[index]; 154 | } 155 | } 156 | } 157 | } 158 | p = n + 1; 159 | } 160 | return true; 161 | } 162 | void Linkmap::insert(size_t index, char *pName, size_t l1, char *pMeth, size_t l2, size_t size) { 163 | auto className = std::string(pName, l1); 164 | auto methName = std::string(pMeth, l2); 165 | auto isInstanceMeth = pMeth[0] == '-'; 166 | std::string catName; 167 | if (auto i = className.find('('); i != std::string::npos) { 168 | if (auto j = className.find(')', i + 1); j != std::string::npos) { 169 | catName = className.substr(i + 1, j - i - 1); 170 | className = className.substr(0, i); 171 | } 172 | } 173 | if (auto i = methName.find_last_of(' '); i != std::string::npos) { 174 | methName = methName.substr(i + 1, methName.size() - i - 2); 175 | } 176 | LMObjCClass *objcClass = nullptr; 177 | auto iter = classes.find(className); 178 | if (iter != classes.end()) { 179 | objcClass = &iter->second; 180 | } else { 181 | auto t = LMObjCClass(); 182 | t.name = className; 183 | t.libName = indexs[index]; 184 | classes[className] = t; 185 | objcClass = &classes.find(className)->second; 186 | } 187 | if (catName.empty()) { 188 | if (isInstanceMeth) { 189 | objcClass->instanceMethods[methName] = size; 190 | } else { 191 | objcClass->classMethods[methName] = size; 192 | } 193 | } else { 194 | auto catClass = objcClass->catWithName(catName.c_str()); 195 | if (catClass->libName.empty()) { 196 | catClass->libName = indexs[index]; 197 | } 198 | if (isInstanceMeth) { 199 | catClass->instanceMethods[methName] = size; 200 | } else { 201 | catClass->classMethods[methName] = size; 202 | } 203 | } 204 | } 205 | void Linkmap::addLibName(size_t index, std::string &&name) { 206 | addLibName(index, name); 207 | } 208 | void Linkmap::addLibName(size_t index, std::string &name) { 209 | indexs[index] = name; 210 | } 211 | LMObjCClass *Linkmap::findClass(const char *name, const char *catName) { 212 | auto iter = classes.find(name); 213 | if (iter == classes.end()) return nullptr; 214 | if (catName) { 215 | for (auto j = iter->second.cats.begin(); j != iter->second.cats.end(); ++j) { 216 | if (j->name.compare(catName) == 0) { 217 | return &(*j); 218 | } 219 | } 220 | } 221 | return &iter->second; 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /snake/linkmap/Linkmap.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // linkmap.hpp 3 | // snake 4 | // 5 | // Created by flexih on 2020/1/22. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #ifndef linkmap_hpp 10 | #define linkmap_hpp 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace snake { 18 | struct LMObjCClass { 19 | std::string name; 20 | std::string libName; 21 | std::vector cats; 22 | std::map instanceMethods; 23 | std::map classMethods; 24 | 25 | LMObjCClass *catWithName(const char *n) { 26 | for (auto iter = cats.begin(); iter != cats.end(); ++iter) { 27 | if (iter->name.compare(n) == 0) { 28 | return &(*iter); 29 | } 30 | } 31 | cats.push_back(LMObjCClass()); 32 | cats.back().name = n; 33 | return &cats.back(); 34 | } 35 | }; 36 | class Linkmap { 37 | public: 38 | ~Linkmap(); 39 | bool read(std::string &path); 40 | const auto& allClasses() const { 41 | return classes; 42 | } 43 | const auto& allProtocols() const { 44 | return protocols; 45 | } 46 | LMObjCClass *findClass(const char *name, const char* catName=nullptr); 47 | private: 48 | bool parse(); 49 | void filter(); 50 | void insert(size_t index, char *pName, size_t l1, char *pMeth, size_t l2, size_t size); 51 | void addLibName(size_t index, std::string &name); 52 | void addLibName(size_t index, std::string &&name); 53 | void *pile {nullptr}; 54 | size_t size {0}; 55 | std::string archName; 56 | std::map indexs; 57 | std::map classes; 58 | std::map protocols; 59 | }; 60 | } 61 | 62 | #endif /* linkmap_hpp */ 63 | -------------------------------------------------------------------------------- /snake/mach-o/Arch.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Arch.m 3 | // snake 4 | // 5 | // Created by flexih on 2020/1/20. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #include "Arch.h" 10 | #include "ObjCRuntime.h" 11 | #include 12 | #include 13 | #include 14 | #include "utility.hpp" 15 | 16 | #define RO_META (1<<0) 17 | #define RO_ROOT (1<<1) 18 | #define RO_HAS_CXX_STRUCTORS (1<<2) 19 | #define FAST_DATA_MASK 0x00007ffffffffff8UL 20 | 21 | namespace snake { 22 | Arch::Arch(struct mach_header_64 *header, const char *beg): mach_header(*header), arch(beg), baseAddr((uintptr_t)-1) { 23 | if (mach_header.magic == MH_CIGAM_64) { 24 | swap_mach_header_64(&mach_header, NX_LittleEndian); 25 | } 26 | internalInstanceSelectors.insert(".cxx_destruct"); 27 | internalClassSelectors.insert("load"); 28 | internalClassSelectors.insert("initialize"); 29 | } 30 | bool Arch::parse() { 31 | if (mach_header.filetype != MH_EXECUTE) { 32 | return false; 33 | } 34 | parseSections(); 35 | handleObjCSections(); 36 | return true; 37 | } 38 | const char *Arch::POINTER(uintptr_t x) { 39 | for (auto loadcommand : allLoadCommdands) { 40 | if (loadcommand->cmd == LC_SEGMENT_64) { 41 | auto segment = (const struct segment_command_64 *)loadcommand; 42 | if (x >= segment->vmaddr && x <= segment->vmaddr + segment->vmsize) { 43 | return arch + x - (segment->vmaddr - baseAddr - segment->fileoff) - baseAddr; 44 | } 45 | } 46 | } 47 | assert(0); 48 | return arch + x - baseAddr; 49 | } 50 | const char *Arch::OFFSET(uintptr_t x) { 51 | return arch + x; 52 | } 53 | void Arch::handleObjCSections() { 54 | handleBindinfo(); 55 | handleClasslist(); 56 | handleCategory(); 57 | handleUsedSelectors(); 58 | handleUsedClasses(); 59 | handleProtocolist(); 60 | } 61 | const struct segment_command_64 *Arch::ObjCSegmentAt(size_t index) { 62 | if (index < allSegments.size()) { 63 | return allSegments[index]; 64 | } 65 | return nullptr; 66 | } 67 | const std::string *Arch::bindinfoSymAt(uintptr_t ptr) { 68 | if (auto iter = bindSyms.find(ptr); iter != bindSyms.end()) { 69 | return &iter->second; 70 | } 71 | return nullptr; 72 | } 73 | void Arch::handleBindinfo() { 74 | auto readULEB128 = [](auto &p){ 75 | uint64_t result = 0; 76 | int bit = 0; 77 | do { 78 | uint64_t slice = *p & 0x7f; 79 | if (bit >= 64 || slice << bit >> bit != slice) { 80 | //"uleb128 too big" 81 | } else { 82 | result |= (slice << bit); 83 | bit += 7; 84 | } 85 | } while (*p++ & 0x80); 86 | return result; 87 | }; 88 | 89 | if (dyldinfo.bind_off && dyldinfo.bind_size) { 90 | const char *bind = OFFSET(dyldinfo.bind_off); 91 | const char *end = bind + dyldinfo.bind_size; 92 | uintptr_t addr = baseAddr; 93 | const char *symbolName = nullptr; 94 | while (bind <= end) { 95 | uint8_t byte = *(uint8_t *)bind++; 96 | uint8_t opcode = byte & BIND_OPCODE_MASK; 97 | uint8_t immediate = byte & BIND_IMMEDIATE_MASK; 98 | switch (opcode) { 99 | case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: { 100 | (void)readULEB128(bind); 101 | break; 102 | } 103 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: { 104 | auto segmentIndex = immediate; 105 | auto val = readULEB128(bind); 106 | //segment.vm_addr+offset 107 | addr = ObjCSegmentAt(segmentIndex)->vmaddr + val; 108 | break; 109 | } 110 | case BIND_OPCODE_ADD_ADDR_ULEB: { 111 | auto val = readULEB128(bind); 112 | addr += val; 113 | break; 114 | } 115 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: 116 | symbolName = bind; 117 | bind += strlen(symbolName) + 1; 118 | break; 119 | case BIND_OPCODE_DO_BIND: 120 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: 121 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: 122 | if (symbolName && addr) { 123 | bindSyms[addr] = symbolName; 124 | } 125 | if (opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) { 126 | auto val = readULEB128(bind); 127 | addr += val; 128 | } 129 | if (opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED) { 130 | addr += immediate * sizeof(uintptr_t); 131 | } 132 | addr += sizeof(uintptr_t); 133 | break; 134 | case BIND_OPCODE_SET_ADDEND_SLEB: { 135 | (void)readULEB128(bind); 136 | break; 137 | } 138 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: { 139 | auto count = readULEB128(bind); 140 | auto skip = readULEB128(bind); 141 | for (auto i = 0; i < count; ++i) { 142 | if (symbolName && addr) { 143 | bindSyms[addr] = symbolName; 144 | } 145 | addr += sizeof(uintptr_t) + skip; 146 | } 147 | break; 148 | } 149 | case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: 150 | case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: 151 | case BIND_OPCODE_SET_TYPE_IMM: 152 | case BIND_OPCODE_DONE: 153 | default: 154 | break; 155 | } 156 | } 157 | } 158 | } 159 | ObjCClass* Arch::ObjCClassForName(const char *name) { 160 | if (auto iter = allClasses.find(name); iter != allClasses.end()) { 161 | return &iter->second; 162 | } 163 | auto objCClass = ObjCClass(); 164 | objCClass.name = name; 165 | allClasses[name] = objCClass; 166 | return &allClasses[name]; 167 | } 168 | ObjCProtol* Arch::ObjCProtolForName(const char *name) { 169 | if (auto iter = allProtols.find(name); iter != allProtols.end()) { 170 | return &iter->second; 171 | } 172 | auto objCProtol = ObjCProtol(); 173 | objCProtol.name = name; 174 | allProtols[name] = objCProtol; 175 | return &allProtols[name]; 176 | } 177 | void Arch::handleClasslist() { 178 | for (auto iter : allSections) { 179 | if (strncmp(iter->sectname, "__objc_classlist", std::size(iter->sectname)) == 0) { 180 | auto count = iter->size / sizeof(uintptr_t); 181 | auto beg = OFFSET(iter->offset); 182 | std::vector classRefs; 183 | for (auto i = 0; i < count; ++i) { 184 | uintptr_t classRef = *(uintptr_t *)(beg + i * sizeof(uintptr_t)); 185 | classRefs.push_back(classRef); 186 | objc_class *oclass = (objc_class *)POINTER(classRef); 187 | if (oclass->isa) { 188 | classRefs.push_back(oclass->isa); 189 | } 190 | } 191 | for (auto classRef : classRefs) { 192 | objc_class *oclass = (objc_class *)POINTER(classRef); 193 | class_ro_t *ro = (class_ro_t *)POINTER(oclass->bits & FAST_DATA_MASK); 194 | auto isMeta = ro->flags & RO_META; 195 | auto objcClass = ObjCClassForName(POINTER(ro->name)); 196 | objcmethdlist(objcClass, ro->baseMethods, isMeta); 197 | objcprotocollist(objcClass, ro->baseProtocols); 198 | if (!isMeta) { 199 | if (oclass->superclass) { 200 | objc_class *superclass = (objc_class *)POINTER(oclass->superclass); 201 | class_ro_t *ro = (class_ro_t *)POINTER(superclass->bits & FAST_DATA_MASK); 202 | objcClass->super = POINTER(ro->name); 203 | } else if (auto n = bindinfoSymAt(classRef + sizeof(uintptr_t))) { 204 | if (n->size() > 14 && n->find("_OBJC_CLASS_$_") == 0) { 205 | objcClass->super = n->substr(14); 206 | } else { 207 | objcClass->super = *n; 208 | } 209 | } 210 | } 211 | } 212 | break; 213 | } 214 | } 215 | } 216 | void Arch::objcmethdlist(ObjCClass *objcClass, uintptr_t list, bool isMeta) { 217 | if (list) { 218 | auto methodList = (struct method_list_t *)POINTER(list); 219 | for (auto j = 0; j < methodList->method_count; ++j) { 220 | auto m = (struct method_t *)&methodList->method_list + j; 221 | auto name = std::string(POINTER(m->name)); 222 | if (isMeta) { 223 | if (!objcClass->classMethods.insert(std::move(name)).second) { 224 | objcClass->classMethodsDup.insert(std::move(name)); 225 | } 226 | } else { 227 | if (!objcClass->instanceMethods.insert(std::move(name)).second) { 228 | objcClass->instanceMethodsDup.insert(std::move(name)); 229 | } 230 | } 231 | } 232 | } 233 | } 234 | void Arch::objcprotocollist(ObjCClass *objcClass, uintptr_t list) { 235 | if (list) { 236 | auto protocolList = (struct protocol_list_t*)POINTER(list); 237 | auto baseprotocol = (uintptr_t *)&protocolList->super_protocols; 238 | for (auto j = 0; j < protocolList->protocol_count; ++j) { 239 | auto protocol = (struct protocol_t *)POINTER(baseprotocol[j]); 240 | objcClass->protocols.insert(POINTER(protocol->name)); 241 | objcprotocollist(objcClass, protocol->protocols); 242 | } 243 | } 244 | } 245 | void Arch::handleUsedSelectors() { 246 | for (auto iter : allSections) { 247 | if (strncmp(iter->sectname, "__objc_selrefs", std::size(iter->sectname)) == 0) { 248 | auto count = iter->size / sizeof(uintptr_t); 249 | for (auto i = 0; i < count; ++i) { 250 | auto beg = OFFSET(iter->offset); 251 | uintptr_t methodNamePtr = *(uintptr_t *)(beg + i * sizeof(uintptr_t)); 252 | usedSelectors.insert(POINTER(methodNamePtr)); 253 | } 254 | break; 255 | } 256 | } 257 | } 258 | void Arch::handleUsedClasses() { 259 | for (auto iter : allSections) { 260 | if ((strncmp(iter->sectname, "__objc_classrefs", std::size(iter->sectname)) == 0 || 261 | strncmp(iter->sectname, "__objc_superrefs", std::size(iter->sectname)) == 0)) { 262 | auto count = iter->size / sizeof(uintptr_t); 263 | auto beg = (uintptr_t *)OFFSET(iter->offset); 264 | for (auto i = 0; i < count; ++i) { 265 | uintptr_t classRef = beg[i]; 266 | if (classRef) { 267 | objc_class *oclass = (objc_class *)POINTER(classRef); 268 | class_ro_t *ro = (class_ro_t *)POINTER(oclass->bits & FAST_DATA_MASK); 269 | refedClasses.insert(POINTER(ro->name)); 270 | } else { 271 | //pointer(offset+base_addr) in bind info 272 | auto offset = iter->offset + i * sizeof(uintptr_t); 273 | auto ptr = offset + baseAddr; 274 | if (auto n = bindinfoSymAt(ptr)) { 275 | if (n->size() > 14 && n->find("_OBJC_CLASS_$_") == 0) { 276 | refedClasses.insert(n->substr(14)); 277 | } else { 278 | refedClasses.insert(*n); 279 | } 280 | } 281 | } 282 | } 283 | break; 284 | } 285 | } 286 | } 287 | void Arch::handleProtocolist() { 288 | for (auto iter : allSections) { 289 | if (strncmp(iter->sectname, "__objc_protolist", std::size(iter->sectname)) == 0) { 290 | auto count = iter->size / sizeof(uintptr_t); 291 | auto protocolRefPtr = (uintptr_t *)OFFSET(iter->offset); 292 | for (auto i = 0; i < count; ++i) { 293 | auto protocol = (struct protocol_t *)POINTER(protocolRefPtr[i]); 294 | auto objcProtol = ObjCProtolForName(POINTER(protocol->name)); 295 | 296 | #define _each_methodlist(x, y) do { \ 297 | if (protocol->x) { \ 298 | auto methodList = (struct method_list_t *)POINTER(protocol->x); \ 299 | for (auto j = 0; j < methodList->method_count; ++j) { \ 300 | struct method_t *m = (struct method_t *)&(methodList->method_list) + j; \ 301 | objcProtol->y.insert(POINTER(m->name)); \ 302 | } \ 303 | }\ 304 | } while (0) 305 | #define each_methodlist(x) _each_methodlist(x, x) 306 | 307 | each_methodlist(instanceMethods); 308 | each_methodlist(classMethods); 309 | _each_methodlist(optionalInstanceMethods, instanceMethods); 310 | _each_methodlist(optionalClassMethods, classMethods); 311 | } 312 | break; 313 | } 314 | } 315 | } 316 | void Arch::handleCategory() { 317 | for (auto iter : allSections) { 318 | if (strncmp(iter->sectname, "__objc_catlist", std::size(iter->sectname)) == 0) { 319 | auto count = iter->size / sizeof(uintptr_t); 320 | auto catRefPtr = (uintptr_t *)OFFSET(iter->offset); 321 | for (auto i = 0; i < count; ++i) { 322 | ObjCClass *objcClass = nullptr; 323 | ObjCClass *catClass = nullptr; 324 | auto cat = (struct category_t *)POINTER(catRefPtr[i]); 325 | if (cat->cls) { 326 | objc_class *oclass = (objc_class *)POINTER(cat->cls); 327 | class_ro_t *ro = (class_ro_t *)POINTER(oclass->bits & FAST_DATA_MASK); 328 | objcClass = ObjCClassForName(POINTER(ro->name)); 329 | } else if (auto n = bindinfoSymAt(catRefPtr[i] + sizeof(uintptr_t))) { 330 | if (auto i = n->find("_OBJC_CLASS_$_"); i != std::string::npos) { 331 | objcClass = ObjCClassForName((n->substr(i + 14)).c_str()); 332 | objcClass->externed = true; 333 | } else { 334 | objcClass = ObjCClassForName(n->c_str()); 335 | } 336 | } 337 | catClass = objcClass->catWithName(POINTER(cat->name)); 338 | objcprotocollist(objcClass, cat->protocols); 339 | objcprotocollist(catClass, cat->protocols); 340 | objcmethdlist(catClass, cat->instanceMethods, false); 341 | objcmethdlist(catClass, cat->classMethods, true); 342 | } 343 | break; 344 | } 345 | } 346 | } 347 | void Arch::handleSymtab() { 348 | auto nlistRef = (struct nlist_64 *)OFFSET(symtab.symoff); 349 | auto strlist = OFFSET(symtab.stroff); 350 | for (auto i = 0; i < symtab.nsyms; ++i) { 351 | auto n_list = nlistRef[i]; 352 | if (n_list.n_sect != NO_SECT && n_list.n_value > 0) { 353 | symtabs[n_list.n_value] = strlist + n_list.n_un.n_strx; 354 | } 355 | } 356 | } 357 | std::vector Arch::handleDyld() { 358 | std::vector result; 359 | size_t offset = sizeof(mach_header); 360 | for (auto loadcommand : allLoadCommdands) { 361 | if (loadcommand->cmd == LC_LOAD_DYLIB) { 362 | auto dylib_command = (struct dylib_command *)loadcommand; 363 | auto name = OFFSET(offset) + dylib_command->dylib.name.offset; 364 | result.push_back(name); 365 | } 366 | offset += loadcommand->cmdsize; 367 | } 368 | return result; 369 | } 370 | void Arch::parseSections() { 371 | size_t offset = 0; 372 | const char *beg = OFFSET(sizeof(mach_header)); 373 | for (size_t i = 0; i < mach_header.ncmds; ++i) { 374 | auto loadcommand = (const struct load_command*)(beg + offset); 375 | switch (loadcommand->cmd) { 376 | case LC_SEGMENT_64: { 377 | auto segment = (const struct segment_command_64 *)loadcommand; 378 | allSegments.push_back(segment); 379 | for (size_t i = 0; i < segment->nsects; ++i) { 380 | auto section = (const struct section_64 *)(segment + 1) + i; 381 | allSections.push_back(section); 382 | } 383 | if (segment->filesize > 0 && segment->vmaddr < baseAddr) { 384 | baseAddr = segment->vmaddr; 385 | } 386 | break; 387 | } 388 | case LC_SYMTAB: 389 | symtab = *(struct symtab_command *)loadcommand; 390 | break; 391 | case LC_DYLD_INFO: 392 | case LC_DYLD_INFO_ONLY: 393 | dyldinfo = *(struct dyld_info_command *)loadcommand; 394 | default: 395 | break; 396 | } 397 | allLoadCommdands.push_back(loadcommand); 398 | offset += loadcommand->cmdsize; 399 | } 400 | } 401 | std::vector Arch::ObjCClasses() const { 402 | std::vector keys; 403 | keys.reserve(allClasses.size()); 404 | for (auto &pair : allClasses) { 405 | if (!pair.second.externed) { 406 | keys.push_back(pair.first); 407 | } 408 | } 409 | return keys; 410 | } 411 | std::vector Arch::ObjCProtocols() const { 412 | std::vector keys; 413 | keys.reserve(allProtols.size()); 414 | for (auto &pair : allProtols) { 415 | keys.push_back(pair.first); 416 | } 417 | return keys; 418 | } 419 | std::set Arch::ObjCProtocolsUsed() const { 420 | std::set protocols; 421 | for (auto &className : ObjCClassesUsed()) { 422 | if (auto iter = allClasses.find(className); iter != allClasses.end()) { 423 | protocols.insert(iter->second.protocols.begin(), iter->second.protocols.end()); 424 | } 425 | } 426 | return protocols; 427 | } 428 | std::vector Arch::ObjCSelectors() const { 429 | std::vector keys; 430 | for (auto &pair : allClasses) { 431 | auto objcClass = pair.second; 432 | for_each(objcClass.classMethods.begin(), objcClass.classMethods.end(), [&keys, &objcClass](auto &k){ 433 | std::stringstream os; 434 | os << '+' << '[' << objcClass.name << ' ' << k << ']'; 435 | keys.push_back(os.str()); 436 | }); 437 | for_each(objcClass.instanceMethods.begin(), objcClass.instanceMethods.end(), [&keys, &objcClass](auto &k){ 438 | std::stringstream os; 439 | os << '-' << '[' << objcClass.name << ' ' << k << ']'; 440 | keys.push_back(os.str()); 441 | }); 442 | } 443 | return keys; 444 | } 445 | std::set Arch::ObjCClassesUsed() const { 446 | auto result = refedClasses; 447 | for (auto &c : refedClasses) { 448 | const std::string *pName = &c; 449 | while (1) { 450 | auto iter = allClasses.find(*pName); 451 | if (iter != allClasses.end() && !iter->second.super.empty() && !contains(result, iter->second.super)) { 452 | result.insert(iter->second.super); 453 | pName = &iter->second.super; 454 | } else { 455 | break; 456 | } 457 | } 458 | } 459 | return result; 460 | } 461 | std::vector Arch::ObjCClassesUnused() const { 462 | auto classes = ObjCClasses(); 463 | std::vector unused; 464 | unused.reserve(classes.size()); 465 | auto usedClasses = ObjCClassesUsed(); 466 | for (auto &c : classes) { 467 | if (!contains(usedClasses, c)) { 468 | unused.push_back(c); 469 | } 470 | } 471 | return unused; 472 | } 473 | std::vector Arch::ObjCProtocolsUnused() const { 474 | auto all = ObjCProtocols(); 475 | auto used = ObjCProtocolsUsed(); 476 | std::vector result; 477 | result.reserve(all.size() - used.size()); 478 | std::set_difference(all.begin(), all.end(), used.begin(), used.end(), std::back_inserter(result)); 479 | return result; 480 | } 481 | std::map Arch::ObjCSelectorsUnused() const { 482 | //class.selector-protocol.selector 483 | //-used.selector-internalSelectors 484 | auto result = allClasses; 485 | auto instanceMethodFilter = [&](auto &m) { 486 | return usedSelectors.find(m) != usedSelectors.end() || internalInstanceSelectors.find(m) != internalInstanceSelectors.end(); 487 | }; 488 | auto classMethodFilter = [&](auto &m) { 489 | return usedSelectors.find(m) != usedSelectors.end() || internalClassSelectors.find(m) != internalClassSelectors.end(); 490 | }; 491 | for (auto iter = result.begin(); iter != result.end();) { 492 | auto &objcClass = iter->second; 493 | for (auto &name : objcClass.protocols) { 494 | auto protocol = allProtols.at(name); 495 | discard_if(objcClass.instanceMethods, [&protocol](auto &m) { 496 | return contains(protocol.instanceMethods, m); 497 | }); 498 | discard_if(objcClass.classMethods, [&protocol](auto &m){ 499 | return contains(protocol.classMethods, m); 500 | }); 501 | for_each(objcClass.cats.begin(), objcClass.cats.end(), [&](auto &oc){ 502 | discard_if(oc.instanceMethods, [&](auto &m){ 503 | return contains(protocol.instanceMethods, m); 504 | }); 505 | discard_if(oc.classMethods, [&](auto &m){ 506 | return contains(protocol.classMethods, m); 507 | }); 508 | }); 509 | } 510 | discard_if(objcClass.instanceMethods, instanceMethodFilter); 511 | discard_if(objcClass.classMethods, classMethodFilter); 512 | for_each(objcClass.cats.begin(), objcClass.cats.end(), [&](auto &oc) { 513 | discard_if(oc.instanceMethods, instanceMethodFilter); 514 | discard_if(oc.classMethods, classMethodFilter); 515 | }); 516 | for (auto i = objcClass.cats.begin(); i != objcClass.cats.end();) { 517 | if (i->empty()) { 518 | i = objcClass.cats.erase(i); 519 | } else { 520 | ++i; 521 | } 522 | } 523 | if (objcClass.empty()) { 524 | iter = result.erase(iter); 525 | } else { 526 | ++iter; 527 | } 528 | } 529 | return result; 530 | } 531 | std::vector Arch::ObjCDuplicateSelectors() const { 532 | std::vector result; 533 | for (auto &pair : allClasses) { 534 | std::vector parts; 535 | const auto &objcClass = pair.second; 536 | for (const auto &cat : objcClass.cats) { 537 | for (const auto &name : cat.instanceMethodsDup) { 538 | std::stringstream os; 539 | os << '-' << '[' << objcClass.name << '(' << cat.name << ')' << ' ' << name << ']'; 540 | result.push_back(os.str()); 541 | } 542 | for (const auto &name : cat.classMethodsDup) { 543 | std::stringstream os; 544 | os << '+' << '[' << objcClass.name << '(' << cat.name << ')' << ' ' << name << ']'; 545 | result.push_back(os.str()); 546 | } 547 | parts.push_back(&cat); 548 | } 549 | parts.push_back(&pair.second); 550 | std::vector output; 551 | for (auto i = 0; i < parts.size() - 1; ++i) { 552 | for (auto j = i + 1; j < parts.size(); ++j) { 553 | std::set_intersection(parts[i]->instanceMethods.begin(), parts[i]->instanceMethods.end(), 554 | parts[j]->instanceMethods.begin(), parts[j]->instanceMethods.end(), std::back_inserter(output)); 555 | if (!output.empty()) { 556 | for (auto &name : output) { 557 | std::stringstream os; 558 | if (i == 0) { 559 | os << '-' << '[' << objcClass.name << ' ' << name << ']'; 560 | } else { 561 | os << '-' << '[' << objcClass.name << '(' << parts[i]->name << ')' << ' ' << name << ']'; 562 | } 563 | os << " == "; 564 | os << '-' << '[' << objcClass.name << '(' << parts[j]->name << ')' << ' ' << name << ']'; 565 | result.push_back(os.str()); 566 | } 567 | output.clear(); 568 | } 569 | std::set_intersection(parts[i]->classMethods.begin(), parts[i]->classMethods.end(), 570 | parts[j]->classMethods.begin(), parts[j]->classMethods.end(), std::back_inserter(output)); 571 | if (!output.empty()) { 572 | for (auto &name : output) { 573 | std::stringstream os; 574 | if (i == 0) { 575 | os << '+' << '[' << objcClass.name << ' ' << name << ']'; 576 | } else { 577 | os << '+' << '[' << objcClass.name << '(' << parts[i]->name << ')' << ' ' << name << ']'; 578 | } 579 | os << " == "; 580 | os << '+' << '[' << objcClass.name << '(' << parts[j]->name << ')' << ' ' << name << ']'; 581 | result.push_back(os.str()); 582 | } 583 | output.clear(); 584 | } 585 | } 586 | } 587 | } 588 | return result; 589 | } 590 | } 591 | 592 | -------------------------------------------------------------------------------- /snake/mach-o/Arch.h: -------------------------------------------------------------------------------- 1 | // 2 | // Arch.h 3 | // snake 4 | // 5 | // Created by flexih on 2020/1/20. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "ObjCClass.hpp" 15 | 16 | namespace snake { 17 | class Arch { 18 | public: 19 | Arch(struct mach_header_64 *mach_header, const char *beg); 20 | bool parse(); 21 | std::vector ObjCClasses() const; 22 | std::vector ObjCProtocols() const; 23 | std::vector ObjCSelectors() const; 24 | std::vector ObjCClassesUnused() const; 25 | std::vector ObjCProtocolsUnused() const; 26 | std::vector ObjCDuplicateSelectors() const; 27 | std::set ObjCProtocolsUsed() const; 28 | std::set ObjCClassesUsed() const; 29 | std::map ObjCSelectorsUnused() const; 30 | private: 31 | void parseSections(); 32 | void handleBindinfo(); 33 | void handleClasslist(); 34 | void handleUsedSelectors(); 35 | void handleProtocolist(); 36 | void handleUsedClasses(); 37 | void handleCategory(); 38 | void handleSymtab(); 39 | std::vector handleDyld(); 40 | void handleObjCSections(); 41 | const char *POINTER(uintptr_t x); 42 | const char *OFFSET(uintptr_t x); 43 | ObjCClass* ObjCClassForName(const char *name); 44 | ObjCProtol* ObjCProtolForName(const char *name); 45 | void objcprotocollist(ObjCClass *oclass, uintptr_t protocolList); 46 | void objcmethdlist(ObjCClass *objcClass, uintptr_t list, bool isMeta); 47 | const struct segment_command_64 *ObjCSegmentAt(size_t index); 48 | const std::string *bindinfoSymAt(uintptr_t ptr); 49 | 50 | const char *arch; 51 | uintptr_t baseAddr; 52 | struct mach_header_64 mach_header; 53 | struct segment_command_64 linkedit; 54 | struct symtab_command symtab; 55 | struct dyld_info_command dyldinfo; 56 | 57 | std::vector allLoadCommdands; 58 | std::vector allSegments; 59 | std::vector allSections; 60 | 61 | std::map allClasses; 62 | std::map allProtols; 63 | std::set usedSelectors; 64 | std::set refedClasses; 65 | std::set internalInstanceSelectors; 66 | std::set internalClassSelectors; 67 | 68 | std::map bindSyms; 69 | std::map symtabs; 70 | }; 71 | } 72 | -------------------------------------------------------------------------------- /snake/mach-o/Bin.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Bin.m 3 | // snake 4 | // 5 | // Created by flexih on 2020/1/20. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #include "Bin.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace snake { 21 | Bin::~Bin() { 22 | if (pile) { 23 | munmap(pile, size); 24 | } 25 | } 26 | bool Bin::read(std::string &path) { 27 | if (path.empty()) return false; 28 | if (pile) { 29 | munmap(pile, size); 30 | pile = nullptr; 31 | } 32 | int fd = open(path.c_str(), O_RDONLY, S_IRUSR | S_IWUSR); 33 | if (fd < 0) { 34 | return false; 35 | } 36 | struct stat st; 37 | fstat(fd, &st); 38 | pile = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0); 39 | close(fd); 40 | size = st.st_size; 41 | return parseArch((char *)pile); 42 | } 43 | bool Bin::parseArch(char *p) { 44 | uint32_t magic = *(uint32_t *)p; 45 | switch (magic) { 46 | case FAT_MAGIC: 47 | case FAT_CIGAM: 48 | case FAT_MAGIC_64: 49 | case FAT_CIGAM_64: { 50 | struct fat_header fat_arch = *(struct fat_header *)p; 51 | if (magic == FAT_CIGAM || magic == FAT_CIGAM_64) { 52 | swap_fat_header(&fat_arch, NX_LittleEndian); 53 | } 54 | p += sizeof(fat_arch); 55 | auto isArch64 = magic == FAT_MAGIC_64 || magic == FAT_CIGAM_64; 56 | for (auto i = 0; i < fat_arch.nfat_arch; ++i) { 57 | if (isArch64) { 58 | auto fat_arch_64 = *(struct fat_arch_64 *)p; 59 | swap_fat_arch_64(&fat_arch_64, 1, NX_LittleEndian); 60 | auto q = fat_arch_64.offset + (char *)pile; 61 | if (parseArch(q)) { 62 | return true; 63 | } 64 | p += sizeof(fat_arch_64); 65 | } else { 66 | auto fat_arch = *(struct fat_arch *)p; 67 | swap_fat_arch(&fat_arch, 1, NX_LittleEndian); 68 | auto q = fat_arch.offset + (char *)pile; 69 | if (parseArch(q)) { 70 | return true; 71 | } 72 | p += sizeof(fat_arch); 73 | } 74 | } 75 | break; 76 | } 77 | case MH_MAGIC_64: 78 | case MH_CIGAM_64: { 79 | Arch arch = Arch((struct mach_header_64 *)p, (const char *)p); 80 | if (!arch.parse()) return false; 81 | archs.push_back(std::move(arch)); 82 | return true; 83 | } 84 | default: 85 | break; 86 | } 87 | return false; 88 | } 89 | bool Bin::isMachO(std::string &path) { 90 | int fd = open(path.c_str(), O_RDONLY, S_IRUSR | S_IWUSR); 91 | if (fd < 0) { 92 | return false; 93 | } 94 | uint32_t magic = 0; 95 | if (::read(fd, &magic, sizeof(magic)) != sizeof(magic)) { 96 | close(fd); 97 | return false; 98 | } 99 | close(fd); 100 | return magic == FAT_MAGIC 101 | || magic == FAT_CIGAM 102 | || magic == FAT_MAGIC_64 103 | || magic == FAT_CIGAM_64 104 | || magic == MH_MAGIC_64 105 | || magic == MH_CIGAM_64; 106 | } 107 | }; 108 | -------------------------------------------------------------------------------- /snake/mach-o/Bin.h: -------------------------------------------------------------------------------- 1 | // 2 | // Bin.h 3 | // snake 4 | // 5 | // Created by flexih on 2020/1/20. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #include "Arch.h" 10 | #include 11 | #include 12 | 13 | namespace snake { 14 | class Bin { 15 | public: 16 | ~Bin(); 17 | bool read(std::string &path); 18 | const Arch* arch() const { 19 | return archs.empty() ? nullptr : &archs.front(); 20 | } 21 | static bool isMachO(std::string &path); 22 | private: 23 | bool parseArch(char *p); 24 | std::vector archs; 25 | void *pile {nullptr}; 26 | size_t size {0}; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /snake/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // main.cpp 3 | // snake 4 | // 5 | // Created by flexih on 2020/1/19. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #include "Bin.h" 10 | #include "Linkmap.hpp" 11 | #include "Output.hpp" 12 | #include "cxxopts.hpp" 13 | #include "utility.hpp" 14 | #include 15 | #include 16 | #include 17 | 18 | using namespace snake; 19 | 20 | std::string findMachOPath(std::string &path) { 21 | if (auto i = path.find_last_of(".app"); i != std::string::npos && i + 1 == path.size()) { 22 | struct stat st; 23 | if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) { 24 | if (auto dir = opendir(path.c_str())) { 25 | struct dirent *dp; 26 | while ((dp = readdir(dir)) != nullptr) { 27 | if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) { 28 | continue; 29 | } 30 | auto sub = path + "/" + dp->d_name; 31 | if (Bin::isMachO(sub)) { 32 | closedir(dir); 33 | return sub; 34 | } 35 | } 36 | closedir(dir); 37 | } 38 | } 39 | } 40 | return path; 41 | } 42 | 43 | int main(int argc, char * argv[]) { 44 | auto useJson = false; 45 | cxxopts::Options options("snake(1.5)", "🐍 Snake, Yet Another Mach-O Unused ObjC Selector/Class/Protocol Detector\n"); 46 | options.custom_help("[-dscp] [-l path] path/to/binary ..."); 47 | options.positional_help(""); 48 | options.add_options() 49 | ("s,selector", "Unused selectors") 50 | ("c,class", "Unused classes") 51 | ("p,protocol", "Unused protocoles") 52 | ("d,duplicate", "Duplicate selectors") 53 | ("a,allclass", "All Classes") 54 | ("l,linkmap", "Linkmap file, which has selector size, library name", cxxopts::value()) 55 | ("i,input", "Mach-O binary", cxxopts::value()) 56 | ("j,json", "Output json format", cxxopts::value(useJson)) 57 | ("help", "Print help") 58 | ; 59 | options.parse_positional({"input"}); 60 | std::string machoPath, linkmapPath; 61 | uint8_t function = 0; 62 | try { 63 | auto result = options.parse(argc, argv); 64 | if (result.count("help")) { 65 | std::cout << options.help() << std::endl; 66 | exit(0); 67 | } 68 | if (result.count("input")) { 69 | machoPath = trimPath(result["input"].as()); 70 | } else { 71 | throw cxxopts::option_not_present_exception("mach-o"); 72 | } 73 | if (result.count("linkmap")) { 74 | linkmapPath = trimPath(result["linkmap"].as()); 75 | } 76 | if (result.count("s")) { 77 | function = 's'; 78 | } else if (result.count("c")) { 79 | function = 'c'; 80 | } else if (result.count("p")) { 81 | function = 'p'; 82 | } else if (result.count("d")) { 83 | function = 'd'; 84 | } else if (result.count("a")) { 85 | function = 'a'; 86 | } else { 87 | throw cxxopts::option_not_present_exception("-dscpa"); 88 | } 89 | machoPath = findMachOPath(machoPath); 90 | Bin bin; 91 | bin.read(machoPath); 92 | Linkmap linkmap; 93 | linkmap.read(linkmapPath); 94 | auto arch = bin.arch(); 95 | if (arch == nullptr) { 96 | std::cout << "snake: " << "mach_header.filetype != MH_EXECUTE" << std::endl; 97 | exit(1); 98 | } 99 | switch (function) { 100 | case 's': { 101 | auto selectorsUnused = arch->ObjCSelectorsUnused(); 102 | std::cout << (useJson ? Output::json.unusedSelectors(selectorsUnused, linkmap).str() + "\n" : Output::raw.unusedSelectors(selectorsUnused, linkmap).str()); 103 | break; 104 | } 105 | case 'c': { 106 | auto classesUnused = arch->ObjCClassesUnused(); 107 | std::cout << (useJson ? Output::json.unusedClasses(classesUnused, linkmap).str() + "\n" : Output::raw.unusedClasses(classesUnused, linkmap).str()); 108 | break; 109 | } 110 | case 'p': { 111 | auto protocolsUnsued = arch->ObjCProtocolsUnused(); 112 | std::cout << (useJson ? Output::json.unusedProtocols(protocolsUnsued, linkmap).str() + "\n" : Output::raw.unusedProtocols(protocolsUnsued, linkmap).str()); 113 | break; 114 | } 115 | case 'd': { 116 | auto duplicateSelectors = arch->ObjCDuplicateSelectors(); 117 | std::cout << Output::raw.duplicatSelectors(duplicateSelectors, linkmap).str() << std::endl; 118 | break; 119 | } 120 | case 'a': { 121 | auto allClasses = arch->ObjCClasses(); 122 | for (auto &c : allClasses) { 123 | std::cout << c << std::endl; 124 | } 125 | break; 126 | } 127 | default: 128 | break; 129 | } 130 | } catch (const cxxopts::OptionException& e) { 131 | std::cout << "snake: " << e.what() << std::endl; 132 | std::cout << options.help() << std::endl; 133 | exit(1); 134 | } 135 | return 0; 136 | } 137 | -------------------------------------------------------------------------------- /snake/objc/ObjCClass.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // ObjCClass.hpp 3 | // snake 4 | // 5 | // Created by flexih on 2020/1/20. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #ifndef ObjCClass_hpp 10 | #define ObjCClass_hpp 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace snake { 18 | struct ObjCClass { 19 | std::string name; 20 | std::string super; 21 | std::vector cats; 22 | std::set protocols; 23 | std::set instanceMethods; 24 | std::set classMethods; 25 | std::set instanceMethodsDup; 26 | std::set classMethodsDup; 27 | bool externed {false}; 28 | 29 | ObjCClass *catWithName(const char *n) { 30 | for (auto iter = cats.begin(); iter != cats.end(); ++iter) { 31 | if (iter->name.compare(n) == 0) { 32 | return &(*iter); 33 | } 34 | } 35 | cats.push_back(ObjCClass()); 36 | cats.back().name = n; 37 | return &cats.back(); 38 | } 39 | 40 | bool empty() const { 41 | if (!instanceMethods.empty()) return false; 42 | if (!classMethods.empty()) return false; 43 | for (auto &oc : cats) { 44 | if (!oc.empty()) { 45 | return false; 46 | } 47 | } 48 | return true; 49 | } 50 | }; 51 | 52 | struct ObjCProtol { 53 | std::string name; 54 | 55 | std::set instanceMethods; 56 | std::set classMethods; 57 | }; 58 | } 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /snake/objc/ObjCRuntime.h: -------------------------------------------------------------------------------- 1 | // 2 | // ObjCRuntime.h 3 | // snake 4 | // 5 | // Created by flexih on 2020/1/19. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #ifndef ObjCRuntime_h 10 | #define ObjCRuntime_h 11 | 12 | #include 13 | #include 14 | 15 | struct class_ro_t_32 { 16 | uint32_t flags; 17 | uint32_t instanceStart; 18 | uint32_t instanceSize; 19 | uint32_t ivarLayout; 20 | uint32_t name; 21 | uint32_t baseMethods; 22 | uint32_t baseProtocols; 23 | uint32_t ivars; 24 | uint32_t weakIvarLayout; 25 | uint32_t baseProperties; 26 | }; 27 | 28 | struct class_ro_t { 29 | uint32_t flags; 30 | uint32_t instanceStart; 31 | uint32_t instanceSize; 32 | uint32_t reserved; 33 | uint64_t ivarLayout; 34 | uint64_t name; 35 | uint64_t baseMethods; 36 | uint64_t baseProtocols; 37 | uint64_t ivars; 38 | uint64_t weakIvarLayout; 39 | uint64_t baseProperties; 40 | }; 41 | 42 | struct objc_class { 43 | uintptr_t isa; 44 | uintptr_t superclass; 45 | uintptr_t cache; 46 | uintptr_t vtable; 47 | uintptr_t bits; 48 | }; 49 | 50 | struct method_t { 51 | uintptr_t name; 52 | uintptr_t types; 53 | uintptr_t imp; 54 | }; 55 | 56 | struct method_list_t { 57 | unsigned int entsize; 58 | unsigned int method_count; 59 | uintptr_t method_list; 60 | }; 61 | 62 | struct protocol_list_t { 63 | uintptr_t protocol_count; 64 | uintptr_t super_protocols; 65 | }; 66 | 67 | struct protocol_t { 68 | uintptr_t isa; 69 | uintptr_t name; 70 | uintptr_t protocols; 71 | uintptr_t instanceMethods; 72 | uintptr_t classMethods; 73 | uintptr_t optionalInstanceMethods; 74 | uintptr_t optionalClassMethods; 75 | uintptr_t instanceProperties; 76 | }; 77 | 78 | struct category_t { 79 | uintptr_t name; 80 | uintptr_t cls; 81 | uintptr_t instanceMethods; 82 | uintptr_t classMethods; 83 | uintptr_t protocols; 84 | uintptr_t instanceProperties; 85 | }; 86 | 87 | struct objc_property { 88 | uintptr_t name; 89 | uintptr_t attributes; 90 | }; 91 | 92 | struct objc_property_list { 93 | uint32_t entsize; 94 | uint32_t count; 95 | }; 96 | 97 | #endif /* ObjCRuntime_h */ 98 | -------------------------------------------------------------------------------- /snake/output/Output.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // output.cpp 3 | // snake 4 | // 5 | // Created by flexih on 2020/1/22. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #include "Output.hpp" 10 | #include "utility.hpp" 11 | 12 | namespace snake { 13 | Raw Output::raw = snake::Raw(); 14 | Json Output::json = snake::Json(); 15 | std::ostringstream Raw::unusedSelectors(std::map &selectorsUnused, Linkmap &linkmap) { 16 | std::ostringstream stream; 17 | auto &allClasses = linkmap.allClasses(); 18 | if (allClasses.empty()) { 19 | stream << "Total Class Count: " << selectorsUnused.size() << std::endl; 20 | size_t count = 0; 21 | for (auto &pair : selectorsUnused) { 22 | count += pair.second.instanceMethods.size() + pair.second.classMethods.size(); 23 | for (auto &i : pair.second.cats) { 24 | count += i.instanceMethods.size() + i .classMethods.size(); 25 | } 26 | } 27 | stream << "Total Unused Selector: " << count << std::endl; 28 | stream << std::endl; 29 | for (auto &pair : selectorsUnused) { 30 | const auto &objcClass = pair.second; 31 | if (!(objcClass.classMethods.empty() && objcClass.instanceMethods.empty())) { 32 | stream << objcClass.name << std::endl; 33 | for_each(objcClass.classMethods.begin(), objcClass.classMethods.end(), [&](auto &m){ 34 | stream << '+' << '[' << objcClass.name << ' ' << m << ']' << std::endl; 35 | }); 36 | for_each(objcClass.instanceMethods.begin(), objcClass.instanceMethods.end(), [&](auto &m){ 37 | stream << '-' << '[' << objcClass.name << ' ' << m << ']' << std::endl; 38 | }); 39 | stream << std::endl; 40 | } 41 | for_each(objcClass.cats.begin(), objcClass.cats.end(), [&](auto &catClass){ 42 | auto catClassName = objcClass.name + '(' + catClass.name + ')'; 43 | stream << catClassName << std::endl; 44 | for_each(catClass.classMethods.begin(), catClass.classMethods.end(), [&](auto &m){ 45 | stream << '+' << '[' << catClassName << ' ' << m << ']' << std::endl; 46 | }); 47 | for_each(catClass.instanceMethods.begin(), catClass.instanceMethods.end(), [&](auto &m){ 48 | stream << '-' << '[' << catClassName << ' ' << m << ']' << std::endl; 49 | }); 50 | stream << std::endl; 51 | }); 52 | } 53 | } else { 54 | const auto &opLibs = interact(selectorsUnused, linkmap); 55 | stream << "Total Lib Count: " << opLibs.size() << std::endl; 56 | stream << "Total Class Count: " << selectorsUnused.size() << std::endl; 57 | size_t count = 0; 58 | for (auto &pair : selectorsUnused) { 59 | count += pair.second.instanceMethods.size() + pair.second.classMethods.size(); 60 | for (auto &i : pair.second.cats) { 61 | count += i.instanceMethods.size() + i .classMethods.size(); 62 | } 63 | } 64 | stream << "Total Unused Selector: " << count << std::endl; 65 | stream << std::endl; 66 | for (auto &pair : opLibs) { 67 | stream << '#' << ' ' << pair.first << std::endl << std::endl; 68 | for (auto &classPair : pair.second) { 69 | stream << '@' << ' ' << classPair.first << std::endl; 70 | for_each(classPair.second.begin(), classPair.second.end(), [&](auto &method){ 71 | stream << method << std::endl; 72 | }); 73 | stream << std::endl; 74 | } 75 | stream << std::endl; 76 | } 77 | } 78 | return stream; 79 | } 80 | std::ostringstream Json::unusedSelectors(std::map &selectorsUnused, Linkmap &linkmap) { 81 | std::ostringstream stream; 82 | auto &allClasses = linkmap.allClasses(); 83 | if (allClasses.empty()) { 84 | stream << '['; 85 | auto i = 0; 86 | for (auto &pair : selectorsUnused) { 87 | const auto &objcClass = pair.second; 88 | auto comma = false; 89 | if (!(objcClass.classMethods.empty() && objcClass.instanceMethods.empty())) { 90 | stream << '{' << "\"class\":" << '\"' << objcClass.name << '\"' << ','; 91 | stream << "\"selectors\":" << '['; 92 | auto j = 0; 93 | for_each(objcClass.classMethods.begin(), objcClass.classMethods.end(), [&](auto &m){ 94 | stream << '\"' << '+' << '[' << objcClass.name << ' ' << m << ']' << '\"'; 95 | if (++j < objcClass.classMethods.size()) { 96 | stream << ','; 97 | } 98 | }); 99 | if (j > 0 && objcClass.instanceMethods.size()) { 100 | stream << ','; 101 | } 102 | j = 0; 103 | for_each(objcClass.instanceMethods.begin(), objcClass.instanceMethods.end(), [&](auto &m){ 104 | stream << '\"' << '-' << '[' << objcClass.name << ' ' << m << ']' << '\"'; 105 | if (++j < objcClass.instanceMethods.size()) { 106 | stream << ','; 107 | } 108 | }); 109 | stream << ']'; 110 | stream << '}'; 111 | comma = true; 112 | } 113 | auto q = 0; 114 | for_each(objcClass.cats.begin(), objcClass.cats.end(), [&](auto &catClass){ 115 | if (catClass.classMethods.empty() && catClass.instanceMethods.empty()) return; 116 | if (comma) { 117 | stream << ','; 118 | comma = false; 119 | } 120 | auto catClassName = objcClass.name + '(' + catClass.name + ')'; 121 | stream << '{' << "\"class\":" << '\"' << catClassName << '\"' << ','; 122 | stream << "\"selectors\":" << '['; 123 | auto j = 0; 124 | for_each(catClass.classMethods.begin(), catClass.classMethods.end(), [&](auto &m){ 125 | stream << '\"' << '+' << '[' << catClassName << ' ' << m << ']' << '\"'; 126 | if (++j < catClass.classMethods.size()) { 127 | stream << ','; 128 | } 129 | }); 130 | if (j > 0 && catClass.instanceMethods.size()) { 131 | stream << ','; 132 | } 133 | j = 0; 134 | for_each(catClass.instanceMethods.begin(), catClass.instanceMethods.end(), [&](auto &m){ 135 | stream << '\"' << '-' << '[' << catClassName << ' ' << m << ']' << '\"'; 136 | if (++j < catClass.instanceMethods.size()) { 137 | stream << ','; 138 | } 139 | }); 140 | stream << ']'; 141 | stream << '}'; 142 | if (++q < objcClass.cats.size()) { 143 | stream << ','; 144 | } 145 | }); 146 | if (++i < selectorsUnused.size()) { 147 | stream << ','; 148 | } 149 | } 150 | stream << ']'; 151 | } else { 152 | const auto &opLibs = interact(selectorsUnused, linkmap); 153 | stream << '['; 154 | auto i = 0; 155 | for (auto &pair : opLibs) { 156 | stream << '{' << "\"lib\":" << '\"' << pair.first << '\"' << ','; 157 | stream << "\"class\":" << '['; 158 | auto j = 0; 159 | for (auto &classPair : pair.second) { 160 | stream << '{' << "\"name\":" << '\"' << classPair.first << '\"' << ','; 161 | stream << "\"selector\":" << '['; 162 | auto q = 0; 163 | for_each(classPair.second.begin(), classPair.second.end(), [&](auto &method){ 164 | auto tab = method.find('\t'); 165 | if (tab == std::string::npos) { 166 | ++q; 167 | return; 168 | } 169 | stream << '{' << "\"name\":" << '\"' << method.substr(0, tab) << '\"' << ','; 170 | stream << "\"size\":" << method.substr(tab + 1) << '}'; 171 | if (++q < classPair.second.size()) { 172 | stream << ','; 173 | } 174 | }); 175 | stream << ']'; 176 | stream << '}'; 177 | if (++j < pair.second.size()) { 178 | stream << ','; 179 | } 180 | } 181 | stream << ']' << '}'; 182 | if (++i < opLibs.size()) { 183 | stream << ','; 184 | } 185 | } 186 | stream << ']'; 187 | } 188 | return stream; 189 | } 190 | const Output::OPLibs Output::interact(std::map &selectorsUnused, Linkmap &linkmap) { 191 | OPLibs opLibs; 192 | 193 | auto getMeths = [&](const std::string &libName, const std::string &className) { 194 | if (auto iter = opLibs.find(libName); iter != opLibs.end()) { 195 | if (auto jter = iter->second.find(className); jter != iter->second.end()) { 196 | return &jter->second; 197 | } else { 198 | iter->second[className] = {}; 199 | } 200 | } else { 201 | opLibs[libName] = {{className, {}}}; 202 | } 203 | return &opLibs.find(libName)->second.find(className)->second; 204 | }; 205 | 206 | auto &allClasses = linkmap.allClasses(); 207 | for (auto &pair : selectorsUnused) { 208 | auto iter = allClasses.find(pair.first); 209 | if (iter == allClasses.end()) { 210 | continue; 211 | } 212 | const auto &lmobjcClass = iter->second; 213 | const auto &objcClass = pair.second; 214 | std::string libName; 215 | std::string className; 216 | { 217 | libName = lmobjcClass.libName; 218 | className = objcClass.name; 219 | size_t size = 0; 220 | for_each(objcClass.instanceMethods.begin(), objcClass.instanceMethods.end(), [&, getMeths](auto &m){ 221 | if (auto jter = lmobjcClass.instanceMethods.find(m); jter != lmobjcClass.instanceMethods.end()) { 222 | size = jter->second; 223 | } 224 | if (size == 0) { 225 | for (auto i = lmobjcClass.cats.begin(); i != lmobjcClass.cats.end(); ++i) { 226 | if (auto qter = i->instanceMethods.find(m); qter != i->instanceMethods.end()) { 227 | size = qter->second; 228 | libName = i->libName; 229 | className = objcClass.name + '(' + i->name + ')'; 230 | break; 231 | } 232 | } 233 | } 234 | std::string method; 235 | method.append(1, '-'); 236 | method.append(1, '['); 237 | method.append(className); 238 | method.append(1, ' '); 239 | method.append(m); 240 | method.append(1, ']'); 241 | if (size) { 242 | method.append(1, '\t'); 243 | method += std::to_string(size); 244 | } 245 | getMeths(libName, className)->push_back(std::move(method)); 246 | }); 247 | } 248 | { 249 | libName = lmobjcClass.libName; 250 | className = objcClass.name; 251 | size_t size = 0; 252 | for_each(objcClass.classMethods.begin(), objcClass.classMethods.end(), [&](auto &m){ 253 | if (auto jter = lmobjcClass.classMethods.find(m); jter != lmobjcClass.classMethods.end()) { 254 | size = jter->second; 255 | } 256 | if (size == 0) { 257 | for (auto i = lmobjcClass.cats.begin(); i != lmobjcClass.cats.end(); ++i) { 258 | if (auto qter = i->classMethods.find(m); qter != i->classMethods.end()) { 259 | size = qter->second; 260 | libName = lmobjcClass.libName; 261 | className = objcClass.name + '(' + i->name + ')'; 262 | break; 263 | } 264 | } 265 | } 266 | std::string method; 267 | method.append(1, '+'); 268 | method.append(1, '['); 269 | method.append(className); 270 | method.append(1, ' '); 271 | method.append(m); 272 | method.append(1, ']'); 273 | if (size) { 274 | method.append(1, '\t'); 275 | method += std::to_string(size); 276 | } 277 | getMeths(libName, className)->push_back(std::move(method)); 278 | }); 279 | } 280 | for_each(objcClass.cats.begin(), objcClass.cats.end(), [&](auto &catClass){ 281 | className = objcClass.name + '(' + catClass.name + ')'; 282 | for (auto i = lmobjcClass.cats.begin(); i != lmobjcClass.cats.end(); ++i) { 283 | if (i->name.compare(catClass.name) != 0) { 284 | continue; 285 | } 286 | libName = i->libName; 287 | { 288 | size_t size = 0; 289 | for_each(catClass.instanceMethods.begin(), catClass.instanceMethods.end(), [&](auto &m){ 290 | if (auto jter = i->instanceMethods.find(m); jter != i->instanceMethods.end()) { 291 | size = jter->second; 292 | } 293 | std::string method; 294 | method.append(1, '-'); 295 | method.append(1, '['); 296 | method.append(className); 297 | method.append(1, ' '); 298 | method.append(m); 299 | method.append(1, ']'); 300 | if (size) { 301 | method.append(1, '\t'); 302 | method += std::to_string(size); 303 | } 304 | getMeths(libName, className)->push_back(std::move(method)); 305 | }); 306 | } 307 | { 308 | size_t size = 0; 309 | for_each(catClass.classMethods.begin(), catClass.classMethods.end(), [&](auto &m){ 310 | if (auto jter = i->classMethods.find(m); jter != i->classMethods.end()) { 311 | size = jter->second; 312 | } 313 | std::string method; 314 | method.append(1, '-'); 315 | method.append(1, '['); 316 | method.append(className); 317 | method.append(1, ' '); 318 | method.append(m); 319 | method.append(1, ']'); 320 | if (size) { 321 | method.append(1, '\t'); 322 | method += std::to_string(size); 323 | } 324 | getMeths(libName, className)->push_back(std::move(method)); 325 | }); 326 | } 327 | break; 328 | } 329 | }); 330 | } 331 | return opLibs; 332 | } 333 | std::ostringstream Raw:: unusedProtocols(std::vector &protocolsUnused, Linkmap &linkmap) { 334 | std::ostringstream stream; 335 | auto &allProtocols = linkmap.allProtocols(); 336 | if (allProtocols.empty()) { 337 | stream << "Total Unused Protocol Count: " << protocolsUnused.size() << std::endl; 338 | stream << std::endl; 339 | for (auto &name : protocolsUnused) { 340 | stream << name << std::endl; 341 | } 342 | } else { 343 | std::map> result; 344 | std::string unknown = "Unknown"; 345 | result[unknown] = {}; 346 | for (auto &name : protocolsUnused) { 347 | const auto *lib = &unknown; 348 | if (auto iter = allProtocols.find(name); iter != allProtocols.end()) { 349 | lib = &iter->second; 350 | } 351 | if (auto iter = result.find(*lib); iter != result.end()) { 352 | iter->second.push_back(name); 353 | } else { 354 | result[*lib] = {name}; 355 | } 356 | } 357 | if (result[unknown].empty()) { 358 | result.erase(unknown); 359 | } 360 | stream << "Total Lib Count: " << result.size() << std::endl; 361 | stream << "Total Unused Protocol Count: " << protocolsUnused.size() << std::endl; 362 | stream << std::endl; 363 | for (auto &pair : result) { 364 | stream << '#' << ' ' << pair.first << std::endl; 365 | for (auto &name : pair.second) { 366 | stream << name << std::endl; 367 | } 368 | stream << std::endl; 369 | } 370 | } 371 | return stream; 372 | } 373 | std::ostringstream Raw::unusedClasses(std::vector &classesUnused, Linkmap &linkmap) { 374 | std::ostringstream stream; 375 | auto &allClasses = linkmap.allClasses(); 376 | if (allClasses.empty()) { 377 | stream << "Total Unused Class Count: " << classesUnused.size() << std::endl; 378 | stream << std::endl; 379 | for (auto &name : classesUnused) { 380 | stream << name << std::endl; 381 | } 382 | } else { 383 | std::map> result; 384 | std::string unknown = "Unknown"; 385 | result[unknown] = {}; 386 | for (auto &name : classesUnused) { 387 | const auto *lib = &unknown; 388 | if (auto iter = allClasses.find(name); iter != allClasses.end()) { 389 | lib = &iter->second.libName; 390 | } 391 | if (auto iter = result.find(*lib); iter != result.end()) { 392 | iter->second.push_back(name); 393 | } else { 394 | result[*lib] = {name}; 395 | } 396 | } 397 | if (result[unknown].empty()) { 398 | result.erase(unknown); 399 | } 400 | stream << "Total Lib Count: " << result.size() << std::endl; 401 | stream << "Total Unused Class Count: " << classesUnused.size() << std::endl; 402 | stream << std::endl; 403 | for (auto &pair : result) { 404 | stream << '#' << ' ' << pair.first << std::endl << std::endl; 405 | for (auto &name : pair.second) { 406 | stream << name << std::endl; 407 | } 408 | stream << std::endl; 409 | } 410 | } 411 | return stream; 412 | } 413 | std::ostringstream Json::unusedProtocols(std::vector &protocolsUnused, Linkmap &linkmap) { 414 | std::ostringstream stream; 415 | auto &allProtocols = linkmap.allProtocols(); 416 | if (allProtocols.empty()) { 417 | stream << '['; 418 | stream << std::endl; 419 | auto i = 0; 420 | for (auto &name : protocolsUnused) { 421 | stream << '\"' << name << '\"'; 422 | if (++i < protocolsUnused.size()) { 423 | stream << ','; 424 | } 425 | } 426 | stream << ']'; 427 | } else { 428 | std::map> result; 429 | std::string unknown = "Unknown"; 430 | result[unknown] = {}; 431 | for (auto &name : protocolsUnused) { 432 | const auto *lib = &unknown; 433 | if (auto iter = allProtocols.find(name); iter != allProtocols.end()) { 434 | lib = &iter->second; 435 | } 436 | if (auto iter = result.find(*lib); iter != result.end()) { 437 | iter->second.push_back(name); 438 | } else { 439 | result[*lib] = {name}; 440 | } 441 | } 442 | if (result[unknown].empty()) { 443 | result.erase(unknown); 444 | } 445 | stream << '['; 446 | auto i = 0; 447 | for (auto &pair : result) { 448 | stream << '{' << "\"lib\":" << '\"' << pair.first << '\"' << ','; 449 | stream << "\"protocol\":" << '['; 450 | auto j = 0; 451 | for (auto &name : pair.second) { 452 | stream << '\"' << name << '\"'; 453 | if (++j < pair.second.size()) { 454 | stream << ','; 455 | } 456 | } 457 | stream << ']' << '}'; 458 | if (++i < result.size()) { 459 | stream << ','; 460 | } 461 | } 462 | stream << ']'; 463 | } 464 | return stream; 465 | } 466 | std::ostringstream Json::unusedClasses(std::vector &classesUnused, Linkmap &linkmap) { 467 | std::ostringstream stream; 468 | auto &allClasses = linkmap.allClasses(); 469 | if (allClasses.empty()) { 470 | stream << '['; 471 | stream << std::endl; 472 | auto i = 0; 473 | for (auto &name : classesUnused) { 474 | stream << '\"' << name << '\"'; 475 | if (++i < classesUnused.size()) { 476 | stream << ','; 477 | } 478 | } 479 | stream << ']'; 480 | } else { 481 | std::map> result; 482 | std::string unknown = "Unknown"; 483 | result[unknown] = {}; 484 | for (auto &name : classesUnused) { 485 | const auto *lib = &unknown; 486 | if (auto iter = allClasses.find(name); iter != allClasses.end()) { 487 | lib = &iter->second.libName; 488 | } 489 | if (auto iter = result.find(*lib); iter != result.end()) { 490 | iter->second.push_back(name); 491 | } else { 492 | result[*lib] = {name}; 493 | } 494 | } 495 | if (result[unknown].empty()) { 496 | result.erase(unknown); 497 | } 498 | stream << '['; 499 | auto i = 0; 500 | for (auto &pair : result) { 501 | stream << '{' << "\"lib\":" << '\"' << pair.first << '\"' << ','; 502 | stream << "\"class\":" << '['; 503 | auto j = 0; 504 | for (auto &name : pair.second) { 505 | stream << '\"' << name << '\"'; 506 | if (++j < pair.second.size()) { 507 | stream << ','; 508 | } 509 | } 510 | stream << ']' << '}'; 511 | if (++i < result.size()) { 512 | stream << ','; 513 | } 514 | } 515 | stream << ']'; 516 | } 517 | return stream; 518 | } 519 | std::ostringstream Raw::duplicatSelectors(std::vector &selectors, Linkmap &linkmap) { 520 | std::ostringstream stream; 521 | auto &allClasses = linkmap.allClasses(); 522 | auto parseSelector = [&](const std::string &selector) { 523 | char className[255], methName[255], catName[255]; 524 | char type; 525 | if (sscanf(selector.c_str(), "%[-+][%[^ ] %[^]]", &type, className, methName) == 3) { 526 | if (char *p = strchr(className, '(')) { 527 | auto len = strlen(className) + className - 1 - (p + 1); 528 | strncpy(catName, p + 1, len); 529 | catName[len] = '\0'; 530 | p[0] = '\0'; 531 | if (auto lmClass = linkmap.findClass(className, catName)) { 532 | std::stringstream os; 533 | os << type << '[' << className << '(' << catName << ')' << "#" << lmClass->libName << ' ' << methName << ']'; 534 | return os.str(); 535 | } 536 | } else { 537 | if (auto lmClass = linkmap.findClass(className)) { 538 | std::stringstream os; 539 | os << type << '[' << className << "#" << lmClass->libName << ' ' << methName << ']'; 540 | return os.str(); 541 | } 542 | } 543 | } 544 | return std::string(); 545 | }; 546 | if (allClasses.empty()) { 547 | stream << "Total Duplicate Selector Count: " << selectors.size() << std::endl; 548 | stream << std::endl; 549 | for (auto &name : selectors) { 550 | stream << name << std::endl; 551 | } 552 | } else { 553 | for (auto &line : selectors) { 554 | auto i = line.find(" == "); 555 | if (i == std::string::npos) { 556 | stream << parseSelector(line) << std::endl; 557 | } else { 558 | stream << parseSelector(line.substr(0, i)) << " == " << parseSelector(line.substr(i + 4, std::string::npos)) << std::endl; 559 | } 560 | } 561 | } 562 | return stream; 563 | } 564 | std::ostringstream Raw::printNames(std::vector &classes, Linkmap &linkmap) { 565 | std::ostringstream stream; 566 | stream << "Total Count: " << classes.size() << std::endl; 567 | stream << std::endl; 568 | for (auto &c : classes) { 569 | stream << c << std::endl; 570 | } 571 | return stream; 572 | } 573 | std::ostringstream Json::printNames(std::vector &classes, Linkmap &linkmap) { 574 | std::ostringstream stream; 575 | return stream; 576 | } 577 | std::ostringstream Json::duplicatSelectors(std::vector &selectors, Linkmap &linkmap) { 578 | std::ostringstream stream; 579 | return stream; 580 | } 581 | } 582 | -------------------------------------------------------------------------------- /snake/output/Output.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // output.hpp 3 | // snake 4 | // 5 | // Created by flexih on 2020/1/22. 6 | // Copyright © 2020 flexih. All rights reserved. 7 | // 8 | 9 | #ifndef output_hpp 10 | #define output_hpp 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "Linkmap.hpp" 18 | #include "ObjCClass.hpp" 19 | 20 | namespace snake { 21 | class Raw; 22 | class Json; 23 | class Output { 24 | public: 25 | virtual std::ostringstream unusedSelectors(std::map &selectorsUnused, Linkmap &linkmap) = 0; 26 | virtual std::ostringstream unusedProtocols(std::vector &protocolsUnused, Linkmap &linkmap) = 0; 27 | virtual std::ostringstream unusedClasses(std::vector &classesUnused, Linkmap &linkmap) = 0; 28 | virtual std::ostringstream duplicatSelectors(std::vector &selectors, Linkmap &linkmap) = 0; 29 | virtual std::ostringstream printNames(std::vector &classes, Linkmap &linkmap) = 0; 30 | //(lib->(class->[meth])) 31 | typedef std::map>> OPLibs; 32 | const OPLibs interact(std::map &selectorsUnused, Linkmap &linkmap); 33 | static Raw raw; 34 | static Json json; 35 | }; 36 | 37 | class Raw: Output { 38 | public: 39 | std::ostringstream unusedSelectors(std::map &selectorsUnused, Linkmap &linkmap); 40 | std::ostringstream unusedProtocols(std::vector &protocolsUnused, Linkmap &linkmap); 41 | std::ostringstream unusedClasses(std::vector &classesUnused, Linkmap &linkmap); 42 | std::ostringstream duplicatSelectors(std::vector &selectors, Linkmap &linkmap); 43 | std::ostringstream printNames(std::vector &classes, Linkmap &linkmap); 44 | }; 45 | 46 | class Json: Output { 47 | public: 48 | std::ostringstream unusedSelectors(std::map &selectorsUnused, Linkmap &linkmap); 49 | std::ostringstream unusedProtocols(std::vector &protocolsUnused, Linkmap &linkmap); 50 | std::ostringstream unusedClasses(std::vector &classesUnused, Linkmap &linkmap); 51 | std::ostringstream duplicatSelectors(std::vector &selectors, Linkmap &linkmap); 52 | std::ostringstream printNames(std::vector &classes, Linkmap &linkmap); 53 | }; 54 | } 55 | 56 | #endif /* output_hpp */ 57 | -------------------------------------------------------------------------------- /storyboard/storyboard.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: UTF-8 -*- 3 | 4 | import sys 5 | import os 6 | import xml.sax 7 | 8 | class Vistor(xml.sax.ContentHandler): 9 | def __init__(self): 10 | self.tags = {} 11 | self.actions = [] 12 | self.propertys = [] 13 | self.outlets = [] 14 | self.customClass = '' 15 | def startElement(self, tag, attributes): 16 | if tag == 'viewController': 17 | self.customClass = attributes['customClass'] 18 | self.tags[attributes['id']] = self.customClass 19 | elif tag == 'action' and attributes.has_key('selector'): 20 | self.actions.append((attributes['selector'], attributes['destination'])) 21 | elif tag == 'outlet' and attributes.has_key('property'): 22 | self.propertys.append(attributes['property']) 23 | def endElement(self, tag): 24 | if tag == 'viewController': 25 | self.outlets.append((self.customClass, self.propertys)) 26 | self.propertys = [] 27 | self.customClass = '' 28 | def endDocument(self): 29 | for action in self.actions: 30 | print '-[{0} {1}]'.format(self.tags[action[1]], action[0]) 31 | for outlet in self.outlets: 32 | for prop in outlet[1]: 33 | print '-[{0} set{1}:]'.format(outlet[0], prop.capitalize()) 34 | 35 | def handleStoryboard(path): 36 | parser = xml.sax.make_parser() 37 | parser.setFeature(xml.sax.handler.feature_namespaces, 0) 38 | visitor = Vistor() 39 | parser.setContentHandler(visitor) 40 | parser.parse(path) 41 | 42 | for i in range(1, len(sys.argv)): 43 | if os.path.exists(sys.argv[i]): 44 | handleStoryboard(sys.argv[i]) 45 | --------------------------------------------------------------------------------