├── .arcconfig ├── .gitignore ├── README.md ├── iSee.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ ├── Yangtsing.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ └── bolei.xcuserdatad │ │ └── WorkspaceSettings.xcsettings └── xcuserdata │ ├── Yangtsing.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── iSee.xcscheme │ │ └── xcschememanagement.plist │ └── bolei.xcuserdatad │ └── xcschemes │ ├── iSee.xcscheme │ └── xcschememanagement.plist ├── iSee ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ └── Main.storyboard ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist ├── ViewController.h ├── ViewController.m ├── iSee.pch ├── inc │ └── GSCommonDefine.h ├── main.m └── src │ ├── BasicCategory │ ├── NSData+DDAdditions.h │ ├── NSData+DDAdditions.m │ ├── NSString+Split.h │ └── NSString+Split.m │ ├── Data_IO │ └── FileReader │ │ ├── DDFileReader.h │ │ └── DDFileReader.m │ └── Model │ ├── ExecutableCodeItem.h │ ├── ExecutableCodeItem.m │ ├── GSLinkMapParser.h │ ├── GSLinkMapParser.m │ ├── MethodFileItem.h │ ├── MethodFileItem.m │ ├── ModuleItem.h │ ├── ModuleItem.m │ ├── ObjectFileItem.h │ ├── ObjectFileItem.m │ ├── ObjectSecionItem.h │ ├── ObjectSecionItem.m │ ├── StaticFileItem.h │ └── StaticFileItem.m ├── iSeeTests ├── Info.plist └── iSeeTests.m ├── iSee_detail.png ├── iSee_step1.png ├── iSee_step2.png ├── iSee_step3.png ├── iSee_step4.png ├── iSee_step5.png └── iSee_step6.png /.arcconfig: -------------------------------------------------------------------------------- 1 | { 2 | "project.name":"iseen", 3 | "phabricator.uri":"https://code-review-bank.phacility.com" 4 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 52 | 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots/**/*.png 56 | fastlane/test_output 57 | 58 | # Code Injection 59 | # 60 | # After new code Injection tools there's a generated folder /iOSInjectionProject 61 | # https://github.com/johnno1962/injectionforxcode 62 | 63 | iOSInjectionProject/ 64 | 65 | **/.ios/**/*.mode1v3 66 | **/.ios/**/*.mode2v3 67 | **/.ios/**/*.moved-aside 68 | **/.ios/**/*.pbxuser 69 | **/.ios/**/*.perspectivev3 70 | **/.ios/**/*sync/ 71 | **/.ios/**/.sconsign.dblite 72 | **/.ios/**/.tags* 73 | **/.ios/**/.vagrant/ 74 | **/.ios/**/DerivedData/ 75 | **/.ios/**/Icon? 76 | **/.ios/**/Pods/ 77 | **/.ios/**/.symlinks/ 78 | **/.ios/**/profile 79 | **/.ios/**/xcuserdata 80 | **/.ios/.generated/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | >> 导读 3 | >> 为了方便对linkMap做解析,参考zyangSir的iSee工程,做了linkMap文件解析的工具,欢迎使用。 4 | >> 5 | 6 | 7 | 8 | # ios包大小分析工具iSee 9 | 10 | ## 简介 11 | 12 | iSee是一款分析iOS可执行文件成分的工具,通过加载XCode在项目编译期间产生的linkMap文件,能够输出项目中每个类(包括第三方静态库),在最终可执行文件中占用的长度信息。本工具根据zyangSir的代码做了部分修改。主要功能有: 13 | 14 | 1. 各个可执行文件占用大小 15 | 2. 可执行文件中,各个段占用的大小(包括方法+常量字符串等) 16 | 3. 未使用到的类 17 | 4. 未使用到的方法 18 | 19 | ![iSee_detai](iSee_detail.png) 20 | 21 | ## 使用方法 22 | 23 | 建议使用真机生成的文件测试,目前看对arm64架构支持最好。优先使用arm64相关文件检测。 24 | 25 | ### 导出生成linkmap文件 26 | 27 | 1. 在XCode编译选项中打开"WriteLinkMapFile" 28 | ![image](./iSee_step1.png) 29 | 30 | 2. 选择好真机/模拟器,Debug/Release。编译项目,进入项目的Derived Data目录 31 | ![image](./iSee_step2.png) 32 | 33 | 3. 依次进入Build/Intermediates/项目名.build/ 目录, 找到相应模式下的编译产物文件夹 34 | ![image](./iSee_step3.png) 35 | 36 | 4. 可以看到一个名为 项目名-LinkMap-normal-CPU架构.txt的文件,在iSee中点击linkMap文件按钮,导入这个文件 37 | 38 | ### 导出生成的可执行文件 39 | 40 | 1. 在上一步的Derived Data目录下, 41 | 42 | ![image](./iSee_step5.png) 43 | 44 | 看到.app结尾的文件,邮件显示包内容,找到和工程名称一样的一个可执行文件,把这个文件copy到相关目录 45 | ![image](./iSee_step5.png) 46 | 47 | 2. 在iSee中点击可执行文件,导入刚才的文件 48 | 3. 之后程序会自动执行分析。 49 | 50 | 51 | ## 简单说明 52 | 53 | 1. 现在对arm64支持比较好,所以建议用这个格式测试。 54 | 2. 对于未使用方法和未使用类,使用otool工具来做辅助分析 55 | 3. 未使用方法和未使用类,有误报可能,主要误报的是一些实现协议的类,这一部分是动态使用的,所以静态分析不出来。 56 | 57 | 详细技术细节可以参考我的博客: 58 | `https://dishibolei.github.io/` 59 | 60 | -------------------------------------------------------------------------------- /iSee.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5608CC661B8DC3240058E846 /* DDFileReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 5608CC651B8DC3240058E846 /* DDFileReader.m */; }; 11 | 5608CC691B8DC3AA0058E846 /* NSData+DDAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 5608CC681B8DC3AA0058E846 /* NSData+DDAdditions.m */; }; 12 | 5608CC781B8EAE6D0058E846 /* GSLinkMapParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 5608CC771B8EAE6D0058E846 /* GSLinkMapParser.m */; }; 13 | 561DDBF41B79FEC100226695 /* ObjectFileItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 561DDBF31B79FEC100226695 /* ObjectFileItem.m */; }; 14 | 561DDBF71B79FF0700226695 /* StaticFileItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 561DDBF61B79FF0700226695 /* StaticFileItem.m */; }; 15 | 561DDBFA1B79FF5900226695 /* MethodFileItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 561DDBF91B79FF5900226695 /* MethodFileItem.m */; }; 16 | 566122DC1B79CCF4001D5057 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 566122DB1B79CCF4001D5057 /* AppDelegate.m */; }; 17 | 566122DE1B79CCF4001D5057 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 566122DD1B79CCF4001D5057 /* main.m */; }; 18 | 566122E11B79CCF4001D5057 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 566122E01B79CCF4001D5057 /* ViewController.m */; }; 19 | 566122E31B79CCF4001D5057 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 566122E21B79CCF4001D5057 /* Images.xcassets */; }; 20 | 566122E61B79CCF4001D5057 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 566122E41B79CCF4001D5057 /* Main.storyboard */; }; 21 | 566122F21B79CCF4001D5057 /* iSeeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 566122F11B79CCF4001D5057 /* iSeeTests.m */; }; 22 | 566B6B101BD8730D00548F4A /* ExecutableCodeItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 566B6B0F1BD8730D00548F4A /* ExecutableCodeItem.m */; }; 23 | 566B6B131BD879DD00548F4A /* NSString+Split.m in Sources */ = {isa = PBXBuildFile; fileRef = 566B6B121BD879DD00548F4A /* NSString+Split.m */; }; 24 | 6E01E8921EF134ED008C1D92 /* ModuleItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E01E8911EF134ED008C1D92 /* ModuleItem.m */; }; 25 | 6E01E8951EF264EA008C1D92 /* ObjectSecionItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E01E8941EF264EA008C1D92 /* ObjectSecionItem.m */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | 566122EC1B79CCF4001D5057 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 566122CD1B79CCF4001D5057 /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 566122D41B79CCF4001D5057; 34 | remoteInfo = iSee; 35 | }; 36 | /* End PBXContainerItemProxy section */ 37 | 38 | /* Begin PBXFileReference section */ 39 | 5608CC1D1B8C15650058E846 /* GSCommonDefine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GSCommonDefine.h; sourceTree = ""; }; 40 | 5608CC1E1B8C15DD0058E846 /* iSee.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iSee.pch; sourceTree = ""; }; 41 | 5608CC641B8DC3240058E846 /* DDFileReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DDFileReader.h; sourceTree = ""; }; 42 | 5608CC651B8DC3240058E846 /* DDFileReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DDFileReader.m; path = FileReader/DDFileReader.m; sourceTree = ""; }; 43 | 5608CC671B8DC3AA0058E846 /* NSData+DDAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+DDAdditions.h"; sourceTree = ""; }; 44 | 5608CC681B8DC3AA0058E846 /* NSData+DDAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+DDAdditions.m"; sourceTree = ""; }; 45 | 5608CC761B8EAE6D0058E846 /* GSLinkMapParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GSLinkMapParser.h; sourceTree = ""; }; 46 | 5608CC771B8EAE6D0058E846 /* GSLinkMapParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GSLinkMapParser.m; sourceTree = ""; }; 47 | 561DDBF21B79FEC100226695 /* ObjectFileItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectFileItem.h; sourceTree = ""; }; 48 | 561DDBF31B79FEC100226695 /* ObjectFileItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjectFileItem.m; sourceTree = ""; }; 49 | 561DDBF51B79FF0700226695 /* StaticFileItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StaticFileItem.h; sourceTree = ""; }; 50 | 561DDBF61B79FF0700226695 /* StaticFileItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StaticFileItem.m; sourceTree = ""; }; 51 | 561DDBF81B79FF5900226695 /* MethodFileItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MethodFileItem.h; sourceTree = ""; }; 52 | 561DDBF91B79FF5900226695 /* MethodFileItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MethodFileItem.m; sourceTree = ""; }; 53 | 566122D51B79CCF4001D5057 /* iSee.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iSee.app; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 566122D91B79CCF4001D5057 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 55 | 566122DA1B79CCF4001D5057 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 56 | 566122DB1B79CCF4001D5057 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 57 | 566122DD1B79CCF4001D5057 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 58 | 566122DF1B79CCF4001D5057 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 59 | 566122E01B79CCF4001D5057 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 60 | 566122E21B79CCF4001D5057 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 61 | 566122E51B79CCF4001D5057 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 62 | 566122EB1B79CCF4001D5057 /* iSeeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iSeeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | 566122F01B79CCF4001D5057 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 64 | 566122F11B79CCF4001D5057 /* iSeeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iSeeTests.m; sourceTree = ""; }; 65 | 566B6B0E1BD8730D00548F4A /* ExecutableCodeItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExecutableCodeItem.h; sourceTree = ""; }; 66 | 566B6B0F1BD8730D00548F4A /* ExecutableCodeItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExecutableCodeItem.m; sourceTree = ""; }; 67 | 566B6B111BD879DD00548F4A /* NSString+Split.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Split.h"; sourceTree = ""; }; 68 | 566B6B121BD879DD00548F4A /* NSString+Split.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Split.m"; sourceTree = ""; }; 69 | 6E01E8901EF134ED008C1D92 /* ModuleItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModuleItem.h; sourceTree = ""; }; 70 | 6E01E8911EF134ED008C1D92 /* ModuleItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ModuleItem.m; sourceTree = ""; }; 71 | 6E01E8931EF264EA008C1D92 /* ObjectSecionItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectSecionItem.h; sourceTree = ""; }; 72 | 6E01E8941EF264EA008C1D92 /* ObjectSecionItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjectSecionItem.m; sourceTree = ""; }; 73 | /* End PBXFileReference section */ 74 | 75 | /* Begin PBXFrameworksBuildPhase section */ 76 | 566122D21B79CCF4001D5057 /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | ); 81 | runOnlyForDeploymentPostprocessing = 0; 82 | }; 83 | 566122E81B79CCF4001D5057 /* Frameworks */ = { 84 | isa = PBXFrameworksBuildPhase; 85 | buildActionMask = 2147483647; 86 | files = ( 87 | ); 88 | runOnlyForDeploymentPostprocessing = 0; 89 | }; 90 | /* End PBXFrameworksBuildPhase section */ 91 | 92 | /* Begin PBXGroup section */ 93 | 5608CC1C1B8C154A0058E846 /* inc */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 5608CC1D1B8C15650058E846 /* GSCommonDefine.h */, 97 | ); 98 | path = inc; 99 | sourceTree = ""; 100 | }; 101 | 5608CC611B8DC10D0058E846 /* Data_IO */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 5608CC651B8DC3240058E846 /* DDFileReader.m */, 105 | 5608CC631B8DC2A40058E846 /* FileReader */, 106 | ); 107 | path = Data_IO; 108 | sourceTree = ""; 109 | }; 110 | 5608CC621B8DC28F0058E846 /* BasicCategory */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 5608CC671B8DC3AA0058E846 /* NSData+DDAdditions.h */, 114 | 5608CC681B8DC3AA0058E846 /* NSData+DDAdditions.m */, 115 | 566B6B111BD879DD00548F4A /* NSString+Split.h */, 116 | 566B6B121BD879DD00548F4A /* NSString+Split.m */, 117 | ); 118 | path = BasicCategory; 119 | sourceTree = ""; 120 | }; 121 | 5608CC631B8DC2A40058E846 /* FileReader */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 5608CC641B8DC3240058E846 /* DDFileReader.h */, 125 | ); 126 | path = FileReader; 127 | sourceTree = ""; 128 | }; 129 | 561DDBF01B79FDB600226695 /* src */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | 5608CC621B8DC28F0058E846 /* BasicCategory */, 133 | 5608CC611B8DC10D0058E846 /* Data_IO */, 134 | 561DDBF11B79FDB600226695 /* Model */, 135 | ); 136 | path = src; 137 | sourceTree = ""; 138 | }; 139 | 561DDBF11B79FDB600226695 /* Model */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | 561DDBF21B79FEC100226695 /* ObjectFileItem.h */, 143 | 561DDBF31B79FEC100226695 /* ObjectFileItem.m */, 144 | 561DDBF51B79FF0700226695 /* StaticFileItem.h */, 145 | 561DDBF61B79FF0700226695 /* StaticFileItem.m */, 146 | 566B6B0E1BD8730D00548F4A /* ExecutableCodeItem.h */, 147 | 566B6B0F1BD8730D00548F4A /* ExecutableCodeItem.m */, 148 | 561DDBF81B79FF5900226695 /* MethodFileItem.h */, 149 | 561DDBF91B79FF5900226695 /* MethodFileItem.m */, 150 | 5608CC761B8EAE6D0058E846 /* GSLinkMapParser.h */, 151 | 5608CC771B8EAE6D0058E846 /* GSLinkMapParser.m */, 152 | 6E01E8901EF134ED008C1D92 /* ModuleItem.h */, 153 | 6E01E8911EF134ED008C1D92 /* ModuleItem.m */, 154 | 6E01E8931EF264EA008C1D92 /* ObjectSecionItem.h */, 155 | 6E01E8941EF264EA008C1D92 /* ObjectSecionItem.m */, 156 | ); 157 | path = Model; 158 | sourceTree = ""; 159 | }; 160 | 566122CC1B79CCF4001D5057 = { 161 | isa = PBXGroup; 162 | children = ( 163 | 566122D71B79CCF4001D5057 /* iSee */, 164 | 566122EE1B79CCF4001D5057 /* iSeeTests */, 165 | 566122D61B79CCF4001D5057 /* Products */, 166 | ); 167 | sourceTree = ""; 168 | }; 169 | 566122D61B79CCF4001D5057 /* Products */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | 566122D51B79CCF4001D5057 /* iSee.app */, 173 | 566122EB1B79CCF4001D5057 /* iSeeTests.xctest */, 174 | ); 175 | name = Products; 176 | sourceTree = ""; 177 | }; 178 | 566122D71B79CCF4001D5057 /* iSee */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | 5608CC1C1B8C154A0058E846 /* inc */, 182 | 561DDBF01B79FDB600226695 /* src */, 183 | 566122DA1B79CCF4001D5057 /* AppDelegate.h */, 184 | 566122DB1B79CCF4001D5057 /* AppDelegate.m */, 185 | 566122DF1B79CCF4001D5057 /* ViewController.h */, 186 | 566122E01B79CCF4001D5057 /* ViewController.m */, 187 | 566122E21B79CCF4001D5057 /* Images.xcassets */, 188 | 566122E41B79CCF4001D5057 /* Main.storyboard */, 189 | 566122D81B79CCF4001D5057 /* Supporting Files */, 190 | ); 191 | path = iSee; 192 | sourceTree = ""; 193 | }; 194 | 566122D81B79CCF4001D5057 /* Supporting Files */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | 566122D91B79CCF4001D5057 /* Info.plist */, 198 | 566122DD1B79CCF4001D5057 /* main.m */, 199 | 5608CC1E1B8C15DD0058E846 /* iSee.pch */, 200 | ); 201 | name = "Supporting Files"; 202 | sourceTree = ""; 203 | }; 204 | 566122EE1B79CCF4001D5057 /* iSeeTests */ = { 205 | isa = PBXGroup; 206 | children = ( 207 | 566122F11B79CCF4001D5057 /* iSeeTests.m */, 208 | 566122EF1B79CCF4001D5057 /* Supporting Files */, 209 | ); 210 | path = iSeeTests; 211 | sourceTree = ""; 212 | }; 213 | 566122EF1B79CCF4001D5057 /* Supporting Files */ = { 214 | isa = PBXGroup; 215 | children = ( 216 | 566122F01B79CCF4001D5057 /* Info.plist */, 217 | ); 218 | name = "Supporting Files"; 219 | sourceTree = ""; 220 | }; 221 | /* End PBXGroup section */ 222 | 223 | /* Begin PBXNativeTarget section */ 224 | 566122D41B79CCF4001D5057 /* iSee */ = { 225 | isa = PBXNativeTarget; 226 | buildConfigurationList = 566122F51B79CCF4001D5057 /* Build configuration list for PBXNativeTarget "iSee" */; 227 | buildPhases = ( 228 | 566122D11B79CCF4001D5057 /* Sources */, 229 | 566122D21B79CCF4001D5057 /* Frameworks */, 230 | 566122D31B79CCF4001D5057 /* Resources */, 231 | ); 232 | buildRules = ( 233 | ); 234 | dependencies = ( 235 | ); 236 | name = iSee; 237 | productName = iSee; 238 | productReference = 566122D51B79CCF4001D5057 /* iSee.app */; 239 | productType = "com.apple.product-type.application"; 240 | }; 241 | 566122EA1B79CCF4001D5057 /* iSeeTests */ = { 242 | isa = PBXNativeTarget; 243 | buildConfigurationList = 566122F81B79CCF4001D5057 /* Build configuration list for PBXNativeTarget "iSeeTests" */; 244 | buildPhases = ( 245 | 566122E71B79CCF4001D5057 /* Sources */, 246 | 566122E81B79CCF4001D5057 /* Frameworks */, 247 | 566122E91B79CCF4001D5057 /* Resources */, 248 | ); 249 | buildRules = ( 250 | ); 251 | dependencies = ( 252 | 566122ED1B79CCF4001D5057 /* PBXTargetDependency */, 253 | ); 254 | name = iSeeTests; 255 | productName = iSeeTests; 256 | productReference = 566122EB1B79CCF4001D5057 /* iSeeTests.xctest */; 257 | productType = "com.apple.product-type.bundle.unit-test"; 258 | }; 259 | /* End PBXNativeTarget section */ 260 | 261 | /* Begin PBXProject section */ 262 | 566122CD1B79CCF4001D5057 /* Project object */ = { 263 | isa = PBXProject; 264 | attributes = { 265 | LastUpgradeCheck = 0640; 266 | ORGANIZATIONNAME = "___zyang.Sir___"; 267 | TargetAttributes = { 268 | 566122D41B79CCF4001D5057 = { 269 | CreatedOnToolsVersion = 6.4; 270 | }; 271 | 566122EA1B79CCF4001D5057 = { 272 | CreatedOnToolsVersion = 6.4; 273 | TestTargetID = 566122D41B79CCF4001D5057; 274 | }; 275 | }; 276 | }; 277 | buildConfigurationList = 566122D01B79CCF4001D5057 /* Build configuration list for PBXProject "iSee" */; 278 | compatibilityVersion = "Xcode 3.2"; 279 | developmentRegion = English; 280 | hasScannedForEncodings = 0; 281 | knownRegions = ( 282 | English, 283 | en, 284 | Base, 285 | ); 286 | mainGroup = 566122CC1B79CCF4001D5057; 287 | productRefGroup = 566122D61B79CCF4001D5057 /* Products */; 288 | projectDirPath = ""; 289 | projectRoot = ""; 290 | targets = ( 291 | 566122D41B79CCF4001D5057 /* iSee */, 292 | 566122EA1B79CCF4001D5057 /* iSeeTests */, 293 | ); 294 | }; 295 | /* End PBXProject section */ 296 | 297 | /* Begin PBXResourcesBuildPhase section */ 298 | 566122D31B79CCF4001D5057 /* Resources */ = { 299 | isa = PBXResourcesBuildPhase; 300 | buildActionMask = 2147483647; 301 | files = ( 302 | 566122E31B79CCF4001D5057 /* Images.xcassets in Resources */, 303 | 566122E61B79CCF4001D5057 /* Main.storyboard in Resources */, 304 | ); 305 | runOnlyForDeploymentPostprocessing = 0; 306 | }; 307 | 566122E91B79CCF4001D5057 /* Resources */ = { 308 | isa = PBXResourcesBuildPhase; 309 | buildActionMask = 2147483647; 310 | files = ( 311 | ); 312 | runOnlyForDeploymentPostprocessing = 0; 313 | }; 314 | /* End PBXResourcesBuildPhase section */ 315 | 316 | /* Begin PBXSourcesBuildPhase section */ 317 | 566122D11B79CCF4001D5057 /* Sources */ = { 318 | isa = PBXSourcesBuildPhase; 319 | buildActionMask = 2147483647; 320 | files = ( 321 | 6E01E8921EF134ED008C1D92 /* ModuleItem.m in Sources */, 322 | 566122E11B79CCF4001D5057 /* ViewController.m in Sources */, 323 | 566B6B101BD8730D00548F4A /* ExecutableCodeItem.m in Sources */, 324 | 561DDBFA1B79FF5900226695 /* MethodFileItem.m in Sources */, 325 | 566B6B131BD879DD00548F4A /* NSString+Split.m in Sources */, 326 | 6E01E8951EF264EA008C1D92 /* ObjectSecionItem.m in Sources */, 327 | 5608CC781B8EAE6D0058E846 /* GSLinkMapParser.m in Sources */, 328 | 5608CC661B8DC3240058E846 /* DDFileReader.m in Sources */, 329 | 561DDBF41B79FEC100226695 /* ObjectFileItem.m in Sources */, 330 | 5608CC691B8DC3AA0058E846 /* NSData+DDAdditions.m in Sources */, 331 | 566122DE1B79CCF4001D5057 /* main.m in Sources */, 332 | 561DDBF71B79FF0700226695 /* StaticFileItem.m in Sources */, 333 | 566122DC1B79CCF4001D5057 /* AppDelegate.m in Sources */, 334 | ); 335 | runOnlyForDeploymentPostprocessing = 0; 336 | }; 337 | 566122E71B79CCF4001D5057 /* Sources */ = { 338 | isa = PBXSourcesBuildPhase; 339 | buildActionMask = 2147483647; 340 | files = ( 341 | 566122F21B79CCF4001D5057 /* iSeeTests.m in Sources */, 342 | ); 343 | runOnlyForDeploymentPostprocessing = 0; 344 | }; 345 | /* End PBXSourcesBuildPhase section */ 346 | 347 | /* Begin PBXTargetDependency section */ 348 | 566122ED1B79CCF4001D5057 /* PBXTargetDependency */ = { 349 | isa = PBXTargetDependency; 350 | target = 566122D41B79CCF4001D5057 /* iSee */; 351 | targetProxy = 566122EC1B79CCF4001D5057 /* PBXContainerItemProxy */; 352 | }; 353 | /* End PBXTargetDependency section */ 354 | 355 | /* Begin PBXVariantGroup section */ 356 | 566122E41B79CCF4001D5057 /* Main.storyboard */ = { 357 | isa = PBXVariantGroup; 358 | children = ( 359 | 566122E51B79CCF4001D5057 /* Base */, 360 | ); 361 | name = Main.storyboard; 362 | sourceTree = ""; 363 | }; 364 | /* End PBXVariantGroup section */ 365 | 366 | /* Begin XCBuildConfiguration section */ 367 | 566122F31B79CCF4001D5057 /* Debug */ = { 368 | isa = XCBuildConfiguration; 369 | buildSettings = { 370 | ALWAYS_SEARCH_USER_PATHS = NO; 371 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 372 | CLANG_CXX_LIBRARY = "libc++"; 373 | CLANG_ENABLE_MODULES = YES; 374 | CLANG_ENABLE_OBJC_ARC = YES; 375 | CLANG_WARN_BOOL_CONVERSION = YES; 376 | CLANG_WARN_CONSTANT_CONVERSION = YES; 377 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 378 | CLANG_WARN_EMPTY_BODY = YES; 379 | CLANG_WARN_ENUM_CONVERSION = YES; 380 | CLANG_WARN_INT_CONVERSION = YES; 381 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 382 | CLANG_WARN_UNREACHABLE_CODE = YES; 383 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 384 | CODE_SIGN_IDENTITY = "-"; 385 | COPY_PHASE_STRIP = NO; 386 | DEBUG_INFORMATION_FORMAT = dwarf; 387 | ENABLE_STRICT_OBJC_MSGSEND = YES; 388 | GCC_C_LANGUAGE_STANDARD = gnu99; 389 | GCC_DYNAMIC_NO_PIC = NO; 390 | GCC_NO_COMMON_BLOCKS = YES; 391 | GCC_OPTIMIZATION_LEVEL = 0; 392 | GCC_PREPROCESSOR_DEFINITIONS = ( 393 | "DEBUG=1", 394 | "$(inherited)", 395 | ); 396 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 397 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 398 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 399 | GCC_WARN_UNDECLARED_SELECTOR = YES; 400 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 401 | GCC_WARN_UNUSED_FUNCTION = YES; 402 | GCC_WARN_UNUSED_VARIABLE = YES; 403 | MACOSX_DEPLOYMENT_TARGET = 11.1; 404 | MTL_ENABLE_DEBUG_INFO = YES; 405 | ONLY_ACTIVE_ARCH = YES; 406 | SDKROOT = macosx; 407 | }; 408 | name = Debug; 409 | }; 410 | 566122F41B79CCF4001D5057 /* Release */ = { 411 | isa = XCBuildConfiguration; 412 | buildSettings = { 413 | ALWAYS_SEARCH_USER_PATHS = NO; 414 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 415 | CLANG_CXX_LIBRARY = "libc++"; 416 | CLANG_ENABLE_MODULES = YES; 417 | CLANG_ENABLE_OBJC_ARC = YES; 418 | CLANG_WARN_BOOL_CONVERSION = YES; 419 | CLANG_WARN_CONSTANT_CONVERSION = YES; 420 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 421 | CLANG_WARN_EMPTY_BODY = YES; 422 | CLANG_WARN_ENUM_CONVERSION = YES; 423 | CLANG_WARN_INT_CONVERSION = YES; 424 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 425 | CLANG_WARN_UNREACHABLE_CODE = YES; 426 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 427 | CODE_SIGN_IDENTITY = "-"; 428 | COPY_PHASE_STRIP = NO; 429 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 430 | ENABLE_NS_ASSERTIONS = NO; 431 | ENABLE_STRICT_OBJC_MSGSEND = YES; 432 | GCC_C_LANGUAGE_STANDARD = gnu99; 433 | GCC_NO_COMMON_BLOCKS = YES; 434 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 435 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 436 | GCC_WARN_UNDECLARED_SELECTOR = YES; 437 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 438 | GCC_WARN_UNUSED_FUNCTION = YES; 439 | GCC_WARN_UNUSED_VARIABLE = YES; 440 | MACOSX_DEPLOYMENT_TARGET = 11.1; 441 | MTL_ENABLE_DEBUG_INFO = NO; 442 | SDKROOT = macosx; 443 | }; 444 | name = Release; 445 | }; 446 | 566122F61B79CCF4001D5057 /* Debug */ = { 447 | isa = XCBuildConfiguration; 448 | buildSettings = { 449 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 450 | COMBINE_HIDPI_IMAGES = YES; 451 | INFOPLIST_FILE = iSee/Info.plist; 452 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 453 | PRODUCT_NAME = "$(TARGET_NAME)"; 454 | }; 455 | name = Debug; 456 | }; 457 | 566122F71B79CCF4001D5057 /* Release */ = { 458 | isa = XCBuildConfiguration; 459 | buildSettings = { 460 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 461 | COMBINE_HIDPI_IMAGES = YES; 462 | INFOPLIST_FILE = iSee/Info.plist; 463 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 464 | PRODUCT_NAME = "$(TARGET_NAME)"; 465 | }; 466 | name = Release; 467 | }; 468 | 566122F91B79CCF4001D5057 /* Debug */ = { 469 | isa = XCBuildConfiguration; 470 | buildSettings = { 471 | BUNDLE_LOADER = "$(TEST_HOST)"; 472 | COMBINE_HIDPI_IMAGES = YES; 473 | FRAMEWORK_SEARCH_PATHS = ( 474 | "$(DEVELOPER_FRAMEWORKS_DIR)", 475 | "$(inherited)", 476 | ); 477 | GCC_PREPROCESSOR_DEFINITIONS = ( 478 | "DEBUG=1", 479 | "$(inherited)", 480 | ); 481 | INFOPLIST_FILE = iSeeTests/Info.plist; 482 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 483 | PRODUCT_NAME = "$(TARGET_NAME)"; 484 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iSee.app/Contents/MacOS/iSee"; 485 | }; 486 | name = Debug; 487 | }; 488 | 566122FA1B79CCF4001D5057 /* Release */ = { 489 | isa = XCBuildConfiguration; 490 | buildSettings = { 491 | BUNDLE_LOADER = "$(TEST_HOST)"; 492 | COMBINE_HIDPI_IMAGES = YES; 493 | FRAMEWORK_SEARCH_PATHS = ( 494 | "$(DEVELOPER_FRAMEWORKS_DIR)", 495 | "$(inherited)", 496 | ); 497 | INFOPLIST_FILE = iSeeTests/Info.plist; 498 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 499 | PRODUCT_NAME = "$(TARGET_NAME)"; 500 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iSee.app/Contents/MacOS/iSee"; 501 | }; 502 | name = Release; 503 | }; 504 | /* End XCBuildConfiguration section */ 505 | 506 | /* Begin XCConfigurationList section */ 507 | 566122D01B79CCF4001D5057 /* Build configuration list for PBXProject "iSee" */ = { 508 | isa = XCConfigurationList; 509 | buildConfigurations = ( 510 | 566122F31B79CCF4001D5057 /* Debug */, 511 | 566122F41B79CCF4001D5057 /* Release */, 512 | ); 513 | defaultConfigurationIsVisible = 0; 514 | defaultConfigurationName = Debug; 515 | }; 516 | 566122F51B79CCF4001D5057 /* Build configuration list for PBXNativeTarget "iSee" */ = { 517 | isa = XCConfigurationList; 518 | buildConfigurations = ( 519 | 566122F61B79CCF4001D5057 /* Debug */, 520 | 566122F71B79CCF4001D5057 /* Release */, 521 | ); 522 | defaultConfigurationIsVisible = 0; 523 | defaultConfigurationName = Debug; 524 | }; 525 | 566122F81B79CCF4001D5057 /* Build configuration list for PBXNativeTarget "iSeeTests" */ = { 526 | isa = XCConfigurationList; 527 | buildConfigurations = ( 528 | 566122F91B79CCF4001D5057 /* Debug */, 529 | 566122FA1B79CCF4001D5057 /* Release */, 530 | ); 531 | defaultConfigurationIsVisible = 0; 532 | defaultConfigurationName = Debug; 533 | }; 534 | /* End XCConfigurationList section */ 535 | }; 536 | rootObject = 566122CD1B79CCF4001D5057 /* Project object */; 537 | } 538 | -------------------------------------------------------------------------------- /iSee.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /iSee.xcodeproj/project.xcworkspace/xcuserdata/Yangtsing.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishibolei/iSee/b8c133420f645d88b8d0ce9e17f660365d204df8/iSee.xcodeproj/project.xcworkspace/xcuserdata/Yangtsing.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /iSee.xcodeproj/project.xcworkspace/xcuserdata/bolei.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildLocationStyle 6 | UseAppPreferences 7 | CustomBuildLocationType 8 | RelativeToDerivedData 9 | DerivedDataLocationStyle 10 | Default 11 | IssueFilterStyle 12 | ShowActiveSchemeOnly 13 | LiveSourceIssuesEnabled 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /iSee.xcodeproj/xcuserdata/Yangtsing.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 18 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /iSee.xcodeproj/xcuserdata/Yangtsing.xcuserdatad/xcschemes/iSee.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 96 | 102 | 103 | 104 | 105 | 107 | 108 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /iSee.xcodeproj/xcuserdata/Yangtsing.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | iSee.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 566122D41B79CCF4001D5057 16 | 17 | primary 18 | 19 | 20 | 566122EA1B79CCF4001D5057 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /iSee.xcodeproj/xcuserdata/bolei.xcuserdatad/xcschemes/iSee.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 64 | 70 | 71 | 72 | 73 | 79 | 81 | 87 | 88 | 89 | 90 | 92 | 93 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /iSee.xcodeproj/xcuserdata/bolei.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | iSee.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 566122D41B79CCF4001D5057 16 | 17 | primary 18 | 19 | 20 | 566122EA1B79CCF4001D5057 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /iSee/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // iSee 4 | // // 入口文件 5 | // Created by Yangtsing.Zhang on 15/8/11. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ViewController.h" 11 | 12 | @interface AppDelegate : NSObject 13 | 14 | // 主viewcontroller入口 15 | @property (nonatomic, strong) IBOutlet ViewController *mainVC; 16 | 17 | @end 18 | 19 | -------------------------------------------------------------------------------- /iSee/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // iSee 4 | // 入口文件 5 | // Created by Yangtsing.Zhang on 15/8/11. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "DDFileReader.h" 11 | #import "GSLinkMapParser.h" 12 | #import "GSCommonDefine.h" 13 | 14 | @interface AppDelegate () 15 | 16 | @property (nonatomic, retain) NSOpenPanel *fileSelectPanel; 17 | 18 | @property (nonatomic, retain) NSString *selectedFilePath; 19 | 20 | @property (nonatomic, retain) DDFileReader *textFileReader; 21 | 22 | @property (nonatomic, retain) GSLinkMapParser *linkMapParser; 23 | 24 | @end 25 | 26 | @implementation AppDelegate 27 | 28 | // ADD Code Review 29 | 30 | - (void)constructManager 31 | { 32 | _fileSelectPanel = [[NSOpenPanel alloc] init]; 33 | [_fileSelectPanel setCanChooseFiles: YES]; 34 | [_fileSelectPanel setCanChooseDirectories: NO]; 35 | [_fileSelectPanel setAllowsMultipleSelection: NO]; 36 | [_fileSelectPanel setTreatsFilePackagesAsDirectories: YES]; 37 | [_fileSelectPanel setAllowedFileTypes:@[@"txt"]]; 38 | } 39 | 40 | - (void)constructFileReader 41 | { 42 | if (_textFileReader) { 43 | self.textFileReader = nil; 44 | } 45 | 46 | _textFileReader = [[DDFileReader alloc] initWithFilePath: _selectedFilePath]; 47 | _linkMapParser.linkMapfileReader = _textFileReader; 48 | } 49 | 50 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 51 | [self constructManager]; 52 | // Insert code here to initialize your application 53 | } 54 | 55 | - (void)applicationWillTerminate:(NSNotification *)aNotification { 56 | // Insert code here to tear down your application 57 | } 58 | 59 | - (void)openDocument:(id)sender 60 | { 61 | [_fileSelectPanel beginSheetModalForWindow: [[NSApplication sharedApplication] mainWindow] completionHandler:^(NSInteger result){ 62 | if (result == NSFileHandlingPanelOKButton) { 63 | NSURL *selectURL = [[self.fileSelectPanel URLs] firstObject]; 64 | [[NSNotificationCenter defaultCenter] postNotificationName: ANALYZE_BEGIN_NOTIFICATION object: [selectURL path]]; 65 | } 66 | }]; 67 | } 68 | 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /iSee/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Gw 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | Default 514 | 515 | 516 | 517 | 518 | 519 | 520 | Left to Right 521 | 522 | 523 | 524 | 525 | 526 | 527 | Right to Left 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | Default 539 | 540 | 541 | 542 | 543 | 544 | 545 | Left to Right 546 | 547 | 548 | 549 | 550 | 551 | 552 | Right to Left 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 798 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 825 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | -------------------------------------------------------------------------------- /iSee/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /iSee/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.baidu.$(PRODUCT_NAME:rfc1034identifier) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2015年 ___zyang.Sir___. All rights reserved. 29 | NSMainStoryboardFile 30 | Main 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /iSee/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/11. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : NSViewController 12 | 13 | @property (strong) IBOutlet NSTextField *arcTypeTextField; 14 | 15 | @property (strong) IBOutlet NSTextView *resultsTextView; 16 | 17 | @property (nonatomic, retain) IBOutlet NSTextField *currentLevelTextField; 18 | 19 | @property (strong, nonatomic) IBOutlet NSProgressIndicator *analyzeProgressBar; 20 | 21 | 22 | @end 23 | 24 | -------------------------------------------------------------------------------- /iSee/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/11. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "ObjectFileItem.h" 11 | #import "GSCommonDefine.h" 12 | #import "DDFileReader.h" 13 | #import "GSLinkMapParser.h" 14 | #import "ModuleItem.h" 15 | #import "ObjectSecionItem.h" 16 | 17 | 18 | 19 | // Constant strings 20 | static NSString * const kTableColumnType = @"typeIdentifier"; 21 | static NSString * const kTableColumnName = @"nameIdentifier"; 22 | static NSString * const kTableColumnSize = @"sizeIdentifier"; 23 | 24 | 25 | typedef enum : NSUInteger { 26 | ESourceSegmentAll = 0, 27 | ESourceSegmentUnusedClass, 28 | ESourceSegmentUnusedSelector, 29 | } ESourceSegment; 30 | 31 | typedef enum : NSUInteger { 32 | EnumLevelAll = 1, 33 | EnumLevelModule, 34 | EnumLevelObjct, 35 | EnumLevelFunction, 36 | } EnumLevel; 37 | 38 | @interface ViewController() 39 | 40 | @property (weak) IBOutlet NSTextField *titleTextField; 41 | 42 | @property (weak) IBOutlet NSButton *backButtion; 43 | 44 | @property (weak) IBOutlet NSTextField *resultTextField; 45 | 46 | @property (weak) IBOutlet NSTableView *tableView; 47 | 48 | @property (weak) IBOutlet NSSegmentedControl *segment; 49 | 50 | @property (weak) IBOutlet NSTextField *excutePathTextField; 51 | 52 | @property (weak) IBOutlet NSTextFieldCell *linkMapPathTextField; 53 | 54 | @property (nonatomic, retain) NSString *linkMapFilePath; 55 | 56 | 57 | @property (strong, nonatomic) NSMutableArray *dataSource; 58 | 59 | @property (strong, nonatomic) NSMutableArray *resultList; 60 | 61 | @property (nonatomic, retain) NSString *selectedFilePath; 62 | 63 | @property (nonatomic, retain) DDFileReader *textFileReader; 64 | 65 | @property (nonatomic, retain) GSLinkMapParser *linkMapParser; 66 | 67 | @property (nonatomic, retain) NSOpenPanel *fileSelectPanel; 68 | 69 | 70 | @property (nonatomic, assign) EnumLevel level; //当前层级 71 | 72 | @property (nonatomic, strong) NSMutableArray *itemsQueue; 73 | 74 | @property (nonatomic, assign) long long totoalSize; 75 | 76 | @property (nonatomic, assign) BOOL fileSizeDesc; 77 | 78 | @property (nonatomic, copy) NSString *excuteFilePath; //可执行文件的地址 79 | 80 | @property (nonatomic, strong) NSMutableArray *unUsedClassList; 81 | 82 | @property (nonatomic, strong) NSMutableArray *unUsedSelectorList; 83 | 84 | @end 85 | 86 | @implementation ViewController 87 | 88 | - (void)dealloc 89 | { 90 | [self unRegistNotification]; 91 | } 92 | 93 | - (void)viewDidLoad { 94 | [super viewDidLoad]; 95 | [self registNotification]; 96 | _resultsTextView.editable = NO; 97 | self.dataSource = [NSMutableArray array]; 98 | self.resultList = [NSMutableArray array]; 99 | self.itemsQueue = [NSMutableArray array]; 100 | self.unUsedClassList = [NSMutableArray array]; 101 | self.unUsedSelectorList = [NSMutableArray array]; 102 | 103 | [self.tableView setDoubleAction:@selector(tableViewDoubleClicked)]; 104 | self.level = EnumLevelAll; 105 | //_analyzeProgressBar.hidden = YES; 106 | // Do any additional setup after loading the view. 107 | } 108 | 109 | - (void)setRepresentedObject:(id)representedObject { 110 | [super setRepresentedObject:representedObject]; 111 | 112 | // Update the view, if already loaded. 113 | } 114 | 115 | #pragma mark - Actions methods 116 | - (IBAction)sortResultsBtnClicked:(NSButton *)sender { 117 | 118 | } 119 | 120 | - (IBAction)resetResultsBtnClicked:(NSButton *)sender { 121 | self.excuteFilePath = @""; 122 | self.excutePathTextField.stringValue = @""; 123 | self.linkMapFilePath = @""; 124 | self.linkMapPathTextField.stringValue = @""; 125 | } 126 | 127 | - (IBAction)backAction:(id)sender { 128 | if (self.level <= EnumLevelAll) { 129 | return; 130 | } 131 | 132 | self.level -= 1; 133 | [self.itemsQueue removeLastObject]; 134 | 135 | [self buildDataSource]; 136 | } 137 | 138 | - (IBAction)excuteFileAction:(id)sender { 139 | [self constructFileSelect]; 140 | [_fileSelectPanel beginSheetModalForWindow: [[NSApplication sharedApplication] mainWindow] completionHandler:^(NSInteger result){ 141 | if (result == NSFileHandlingPanelOKButton) { 142 | NSURL *selectURL = [[self.fileSelectPanel URLs] firstObject]; 143 | self.excuteFilePath = [selectURL path]; 144 | self.excutePathTextField.stringValue = [selectURL path]; 145 | if (self.linkMapFilePath.length > 0) { 146 | [self startAnalyExcuteFile]; 147 | }else { 148 | self.titleTextField.stringValue = @"需要配置LinkMap文件地址"; 149 | 150 | } 151 | 152 | } 153 | }]; 154 | } 155 | - (IBAction)openLinkMapAction:(id)sender { 156 | [self constructFileSelect]; 157 | [_fileSelectPanel beginSheetModalForWindow: [[NSApplication sharedApplication] mainWindow] completionHandler:^(NSInteger result){ 158 | if (result == NSFileHandlingPanelOKButton) { 159 | NSURL *selectURL = [[self.fileSelectPanel URLs] firstObject]; 160 | self.linkMapFilePath = [selectURL path]; 161 | self.linkMapPathTextField.stringValue = [selectURL path]; 162 | 163 | if (self.excuteFilePath.length > 0) { 164 | [self startAnalyExcuteFile]; 165 | }else { 166 | self.titleTextField.stringValue = @"需要配置可执行文件地址"; 167 | } 168 | } 169 | }]; 170 | } 171 | 172 | - (IBAction)switchAction:(id)sender { 173 | [self buildDataSource]; 174 | } 175 | 176 | 177 | #pragma mark - notifications 178 | 179 | - (void)registNotification 180 | { 181 | SEL handler = @selector(onHandleResultsMsgDone:); 182 | [[NSNotificationCenter defaultCenter] addObserver:self selector:handler name: RESULTS_DONE_NOTIFICATION object: nil]; 183 | 184 | SEL arcTypeHandler = @selector(onHandleArchTypeFoundMsg:); 185 | [[NSNotificationCenter defaultCenter] addObserver:self selector:arcTypeHandler name:ARC_TYPE_FIOUND_NOTIFICATION object: nil]; 186 | 187 | SEL progressUpdateHanlder = @selector(onHandleAnalyzeProgressMsg:); 188 | [[NSNotificationCenter defaultCenter] addObserver:self selector: progressUpdateHanlder name: ANALYZE_PROGRESS_UPDATE_NOTIFICATION object: nil]; 189 | 190 | SEL onBeginAnalyzeHanlder = @selector(onBeginAnalyze:); 191 | [[NSNotificationCenter defaultCenter] addObserver:self selector: onBeginAnalyzeHanlder name: ANALYZE_BEGIN_NOTIFICATION object: nil]; 192 | } 193 | 194 | - (void)unRegistNotification 195 | { 196 | [[NSNotificationCenter defaultCenter] removeObserver: self]; 197 | } 198 | 199 | - (void)onHandleResultsMsgDone:(NSNotification *)notification 200 | { 201 | 202 | //清除之前的结果 203 | [self.resultList removeAllObjects]; 204 | 205 | NSMutableArray *objectsArray = notification.object; 206 | 207 | [self.resultList addObjectsFromArray:objectsArray]; 208 | [self buildDataSource]; 209 | 210 | [self startAnylyUnused]; 211 | } 212 | 213 | - (void)onHandleArchTypeFoundMsg:(NSNotification *)notification 214 | { 215 | dispatch_async(dispatch_get_main_queue(), ^{ 216 | [_arcTypeTextField setStringValue: (NSString *)notification.object]; 217 | }); 218 | 219 | } 220 | 221 | - (void)onHandleAnalyzeProgressMsg:(NSNotification *)notification 222 | { 223 | double progress = [(NSNumber *)notification.object doubleValue]; 224 | 225 | progress *= 100;//范围转成0.0 ~ 100.0 226 | dispatch_async(dispatch_get_main_queue(), ^(){ 227 | [_analyzeProgressBar setDoubleValue: progress]; 228 | }); 229 | } 230 | 231 | - (void)onBeginAnalyze:(NSNotification *)notification { 232 | NSString *path = notification.object; 233 | self.selectedFilePath = path; 234 | [self startAnalyze]; 235 | } 236 | 237 | #pragma mark - 238 | 239 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { 240 | return [self.dataSource count]; 241 | } 242 | 243 | - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex { 244 | 245 | if (rowIndex >= [self.dataSource count]) { 246 | return @""; 247 | } 248 | 249 | NSString *columnIdentifier = [tableColumn identifier]; 250 | id obj = [self.dataSource objectAtIndex:rowIndex]; 251 | 252 | 253 | switch (self.level) { 254 | case EnumLevelAll: 255 | case EnumLevelModule: 256 | case EnumLevelObjct: 257 | case EnumLevelFunction:{ 258 | if ([columnIdentifier isEqualToString:kTableColumnType]) { 259 | return [obj valueForKey:@"fileTypeName"]; 260 | } else if ([columnIdentifier isEqualToString:kTableColumnName]) { 261 | return [obj valueForKey:@"name"]; 262 | } else if ([columnIdentifier isEqualToString:kTableColumnSize]) { 263 | return [NSString stringWithFormat:@"%@",[obj valueForKey:@"size"]]; 264 | } 265 | } 266 | break; 267 | default: 268 | break; 269 | } 270 | 271 | return @""; 272 | } 273 | 274 | - (void)tableViewDoubleClicked { 275 | 276 | if (self.segment.selectedSegment == ESourceSegmentAll) { 277 | if (self.level >= EnumLevelFunction) { 278 | return; 279 | } 280 | 281 | self.level += 1; 282 | 283 | id obj = [self.dataSource objectAtIndex:[self.tableView clickedRow]]; 284 | [self.itemsQueue addObject:obj]; 285 | 286 | [self buildDataSource]; 287 | [self.tableView deselectAll:self]; 288 | } 289 | } 290 | 291 | - (void)tableView:(NSTableView *)tableView mouseDownInHeaderOfTableColumn:(NSTableColumn *)tableColumn{ 292 | 293 | NSString *columnIdentifier = [tableColumn identifier]; 294 | if ([columnIdentifier isEqualToString:kTableColumnType]) { 295 | [self.dataSource sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { 296 | return [[obj1 valueForKey:@"fileTypeName"] compare:[obj2 valueForKey:@"fileTypeName"]]; 297 | }]; 298 | } else if ([columnIdentifier isEqualToString:kTableColumnName]) { 299 | [self.dataSource sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { 300 | return [[obj1 valueForKey:@"name"] compare:[obj2 valueForKey:@"name"]]; 301 | }]; 302 | } else if ([columnIdentifier isEqualToString:kTableColumnSize]) { 303 | _fileSizeDesc = !_fileSizeDesc; 304 | [self.dataSource sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { 305 | if (_fileSizeDesc) { 306 | return [[obj2 valueForKey:@"size"] compare:[obj1 valueForKey:@"size"]]; 307 | } 308 | return [[obj1 valueForKey:@"size"] compare:[obj2 valueForKey:@"size"]]; 309 | }]; 310 | } 311 | 312 | [self.tableView reloadData]; 313 | } 314 | 315 | 316 | - (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { 317 | NSTextFieldCell *textField = (NSTextFieldCell *)cell; 318 | textField.textColor = [NSColor blackColor]; 319 | if (self.segment.selectedSegment == ESourceSegmentAll && self.level == EnumLevelModule && [tableColumn.identifier isEqualToString:kTableColumnName]) { 320 | id item = [self.dataSource objectAtIndex:row]; 321 | if ([item isKindOfClass: [ObjectFileItem class]]) { 322 | textField.textColor = ((ObjectFileItem *)item).hasUesd ? [NSColor blackColor] : [NSColor redColor]; 323 | } 324 | } 325 | } 326 | 327 | #pragma mark - Analized 328 | 329 | - (void)startAnalyze { 330 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 331 | [self constructFileReader]; 332 | [self extractLinkMapDataFromFile]; 333 | [self outputAnalysisResults]; 334 | }); 335 | } 336 | 337 | - (void)extractLinkMapDataFromFile 338 | { 339 | NSString *aLineStr = [_textFileReader readLine]; 340 | while (aLineStr && ![_linkMapParser isSectionEndFlag:aLineStr]) { 341 | if ([aLineStr hasPrefix: @"# Arch:"]) {//found code type 342 | NSRange range = [aLineStr rangeOfString: @":"]; 343 | range.location += 2; 344 | range.length = aLineStr.length - range.location; 345 | NSString *arcTypeStr = [[aLineStr substringWithRange: range] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 346 | [[NSNotificationCenter defaultCenter] postNotificationName: ARC_TYPE_FIOUND_NOTIFICATION object: arcTypeStr]; 347 | } 348 | if ([_linkMapParser isSectionStartFlag:aLineStr]) { 349 | 350 | [_linkMapParser startSubParserWithStartFlag: aLineStr]; 351 | aLineStr = [_linkMapParser lastLinkMapLineLog]; 352 | 353 | continue; 354 | } 355 | 356 | aLineStr = [_textFileReader readLine]; 357 | } 358 | [_linkMapParser updateAnalyzeProgress: 1]; 359 | } 360 | 361 | - (void)outputAnalysisResults 362 | { 363 | [_linkMapParser outputObjectFileSize]; 364 | } 365 | 366 | - (void)constructFileReader 367 | { 368 | if (_textFileReader) { 369 | self.textFileReader = nil; 370 | } 371 | _linkMapParser = [[GSLinkMapParser alloc] init]; 372 | _textFileReader = [[DDFileReader alloc] initWithFilePath: _selectedFilePath]; 373 | _linkMapParser.linkMapfileReader = _textFileReader; 374 | 375 | } 376 | 377 | - (void)constructFileSelect 378 | { 379 | if (_fileSelectPanel == nil) { 380 | _fileSelectPanel = [[NSOpenPanel alloc] init]; 381 | [_fileSelectPanel setCanChooseFiles: YES]; 382 | [_fileSelectPanel setCanChooseDirectories: NO]; 383 | [_fileSelectPanel setAllowsMultipleSelection: NO]; 384 | [_fileSelectPanel setTreatsFilePackagesAsDirectories: YES]; 385 | // [_fileSelectPanel setAllowedFileTypes:@[@"txt"]]; 386 | } 387 | } 388 | 389 | - (void)startAnalyExcuteFile { 390 | [[NSNotificationCenter defaultCenter] postNotificationName: ANALYZE_BEGIN_NOTIFICATION object: self.linkMapFilePath]; 391 | } 392 | 393 | - (void)startAnylyUnused { 394 | dispatch_async(dispatch_get_main_queue(), ^{ 395 | if ([self.excuteFilePath length] == 0) { 396 | return; 397 | } 398 | [self.unUsedSelectorList removeAllObjects]; 399 | [self.unUsedClassList removeAllObjects]; 400 | NSString *filePath = self.excuteFilePath; 401 | NSString *arcType = self.arcTypeTextField.stringValue; 402 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 403 | [self startAnalyUsedSelector:filePath archType:arcType]; 404 | [self startAnalyUsedClass:filePath archType:arcType]; 405 | 406 | dispatch_async(dispatch_get_main_queue(), ^{ 407 | self.segment.enabled = YES; 408 | [self buildDataSource]; 409 | }); 410 | }); 411 | }); 412 | 413 | } 414 | 415 | 416 | - (void)startAnalyUsedSelector:(NSString *)filePath archType:(NSString *)arcType { 417 | NSLog(@"startAnalyUsedSelector = %@",filePath); 418 | // 分析未使用的方法 419 | NSTask *task = [[NSTask alloc] init]; 420 | [task setLaunchPath: @"/usr/bin/otool"]; 421 | 422 | NSMutableArray *argvals = [NSMutableArray array]; 423 | 424 | [argvals addObject:@"-V"]; 425 | [argvals addObject:@"-s"]; 426 | [argvals addObject:@"__DATA"]; 427 | [argvals addObject:@"__objc_selrefs"]; 428 | [argvals addObject:filePath]; 429 | [argvals addObject:@"-arch"]; 430 | [argvals addObject:arcType]; 431 | [argvals addObject:@">> ~/Desktop/resultaaa.txt"]; 432 | 433 | [task setArguments:argvals]; 434 | // 运行脚本命令 435 | NSPipe *pipe = [NSPipe pipe]; 436 | [task setStandardOutput: pipe]; 437 | [task setStandardError:pipe]; 438 | NSFileHandle *file = [pipe fileHandleForReading]; 439 | 440 | // Run task 441 | [task launch]; 442 | // Read the response 443 | NSData *data = [file readDataToEndOfFile]; 444 | NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 445 | NSLog(@"string = %@",string); 446 | // See if we can create a lines array 447 | if (string.length) { 448 | [self anylyzeUsedMethodWithData:string]; 449 | } 450 | } 451 | 452 | - (void)startAnalyUsedClass:(NSString *)filePath archType:(NSString *)arcType { 453 | NSLog(@"startAnalyUsedClass = %@",filePath); 454 | NSTask *task = [[NSTask alloc] init]; 455 | [task setLaunchPath: @"/usr/bin/otool"]; 456 | 457 | NSMutableArray *argvals = [NSMutableArray array]; 458 | 459 | [argvals addObject:@"-V"]; 460 | [argvals addObject:@"-o"]; 461 | [argvals addObject:filePath]; 462 | [argvals addObject:@"-arch"]; 463 | [argvals addObject:arcType]; 464 | 465 | [task setArguments:argvals]; 466 | 467 | NSPipe *pipe = [NSPipe pipe]; 468 | [task setStandardOutput: pipe]; 469 | NSFileHandle *file = [pipe fileHandleForReading]; 470 | 471 | // Run task 472 | [task launch]; 473 | 474 | // Read the response 475 | NSData *data = [file readDataToEndOfFile]; 476 | NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 477 | 478 | [self anylyzeUsedClassWithData:string]; 479 | } 480 | 481 | 482 | #pragma mark - DataSource 483 | 484 | - (void)buildDataSource { 485 | dispatch_async(dispatch_get_main_queue(), ^{ 486 | [self.dataSource removeAllObjects]; 487 | 488 | 489 | switch (self.segment.selectedSegment) { 490 | case ESourceSegmentAll: { 491 | [self buildAllSource]; 492 | } 493 | break; 494 | case ESourceSegmentUnusedClass: { 495 | [self buildUnUsedClass]; 496 | } 497 | break; 498 | case ESourceSegmentUnusedSelector: { 499 | [self buildUnUsedSelector]; 500 | } 501 | break; 502 | default: 503 | break; 504 | } 505 | 506 | 507 | [self.dataSource sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { 508 | return [[obj2 valueForKey:@"size"] compare:[obj1 valueForKey:@"size"]]; 509 | }]; 510 | 511 | _fileSizeDesc = YES; 512 | dispatch_async(dispatch_get_main_queue(), ^(){ 513 | [self.tableView reloadData]; 514 | [self.tableView scrollRowToVisible:0]; 515 | [self updateUI]; 516 | }); 517 | }); 518 | 519 | 520 | } 521 | 522 | - (void) buildAllSource { 523 | switch (self.level) { 524 | case EnumLevelAll: { 525 | self.totoalSize = 0; 526 | NSMutableDictionary *resultDic = [NSMutableDictionary dictionaryWithCapacity:[self.resultList count]]; 527 | for (ObjectFileItem *info in self.resultList) { 528 | ModuleItem *item = [resultDic objectForKey:info.module]; 529 | if (item == nil) { 530 | item = [ModuleItem new]; 531 | item.name = info.module; 532 | item.fileTypeName = info.fileTypeName; 533 | [resultDic setObject:item forKey:info.module]; 534 | } 535 | 536 | [item.objectsList addObject:info]; 537 | item.size += info.size; 538 | self.totoalSize += info.size; 539 | } 540 | [self.dataSource addObjectsFromArray:[resultDic allValues]]; 541 | } 542 | 543 | break; 544 | case EnumLevelModule: { 545 | ModuleItem *item = [self.itemsQueue lastObject]; 546 | [self.dataSource addObjectsFromArray:item.objectsList]; 547 | } 548 | 549 | break; 550 | case EnumLevelObjct: { 551 | ObjectFileItem *item = [self.itemsQueue lastObject]; 552 | [self.dataSource addObjectsFromArray:[item.sectionDictionary allValues]]; 553 | } 554 | break; 555 | 556 | case EnumLevelFunction: { 557 | ObjectSecionItem *item = [self.itemsQueue lastObject]; 558 | [self.dataSource addObjectsFromArray:item.objectsList]; 559 | } 560 | break; 561 | default: 562 | break; 563 | } 564 | 565 | } 566 | 567 | - (void)buildUnUsedClass { 568 | [self.dataSource addObjectsFromArray:self.unUsedClassList]; 569 | } 570 | 571 | - (void)buildUnUsedSelector { 572 | [self.dataSource addObjectsFromArray:self.unUsedSelectorList]; 573 | } 574 | 575 | #pragma mark - UI 576 | - (void)updateUI { 577 | self.titleTextField.stringValue = [self getTitle]; 578 | self.resultTextField.stringValue = [self getResult]; 579 | switch (self.level) { 580 | case EnumLevelAll: { 581 | self.backButtion.hidden = YES; 582 | } 583 | break; 584 | case EnumLevelModule: { 585 | self.backButtion.hidden = NO; 586 | } 587 | break; 588 | case EnumLevelObjct: 589 | self.backButtion.hidden = NO; 590 | break; 591 | case EnumLevelFunction: 592 | self.backButtion.hidden = NO; 593 | break; 594 | default: 595 | self.backButtion.hidden = NO; 596 | break; 597 | } 598 | 599 | } 600 | 601 | - (NSString *)getTitle { 602 | NSMutableString *title = [NSMutableString stringWithString:@"总览"]; 603 | for (id obj in self.itemsQueue) { 604 | NSString *name = [obj valueForKey:@"name"]; 605 | [title appendFormat:@"/%@",name]; 606 | } 607 | return title; 608 | } 609 | 610 | - (NSString *)getResult { 611 | id obj = [self.itemsQueue lastObject]; 612 | NSString *size = [NSString stringWithFormat:@"%@",@(self.totoalSize)]; 613 | if (obj) { 614 | size = [NSString stringWithFormat:@"%@",[obj valueForKey:@"size"]]; 615 | } 616 | 617 | return [NSString stringWithFormat:@"分析结果(byte):%@",size]; 618 | } 619 | 620 | # pragma mark - Anylyze 621 | 622 | - (void)anylyzeUsedMethodWithData:(NSString *)string { 623 | // 解析结果,分解数据 624 | if (string.length) { 625 | //数据清空 626 | for (ObjectFileItem *file in self.resultList) { 627 | [file.usedMethod removeAllObjects]; 628 | [file.unUsedMethod removeAllObjects]; 629 | } 630 | 631 | //解析 0000000100dcd9c0 __TEXT:__objc_methname:alloc 632 | NSArray *lines = [string componentsSeparatedByString:@"\n"]; 633 | NSString *regexStr = @"(.+?)\\s+__TEXT:__objc_methname:(.+)"; 634 | NSRegularExpression* regexExpression = [NSRegularExpression regularExpressionWithPattern:regexStr options:NSRegularExpressionCaseInsensitive error:nil]; 635 | 636 | 637 | int objIndex = 0; //扫描的obj的索引 638 | int methodIndex = 0; //扫描到的方法索引 639 | 640 | // 解析出来使用到的方法 641 | for (NSString *line in lines) { 642 | @autoreleasepool { 643 | if (objIndex >= [self.resultList count]) { 644 | break; 645 | } 646 | 647 | NSArray* matchs = [regexExpression matchesInString:line options:0 range:NSMakeRange(0, line.length)]; 648 | 649 | if (matchs == nil || [matchs count] == 0) { 650 | continue; 651 | } 652 | 653 | NSTextCheckingResult *checkingResult = [matchs objectAtIndex:0]; 654 | 655 | if ([checkingResult numberOfRanges] < 3) { 656 | continue; 657 | } 658 | 659 | NSString *startAddressStr = [line substringWithRange:[checkingResult rangeAtIndex:1]]; 660 | NSString *method = [line substringWithRange:[checkingResult rangeAtIndex:2]]; 661 | long startAddress = strtoul([startAddressStr UTF8String], 0, 16); 662 | 663 | 664 | ObjectSecionItem *section = nil; 665 | ObjectFileItem *obj = nil; 666 | 667 | //需要找到对应哪个类的起始地址 668 | while (objIndex < [self.resultList count]) { 669 | obj = [self.resultList objectAtIndex:objIndex]; 670 | section = [obj.sectionDictionary objectForKey:@"__objc_selrefs"]; 671 | MethodFileItem *method = [section.objectsList lastObject]; 672 | if (method.startAddress < startAddress) { 673 | objIndex ++; 674 | methodIndex = 0; 675 | }else { 676 | break; 677 | } 678 | } 679 | 680 | if (objIndex >= [self.resultList count]) { 681 | break; 682 | } 683 | 684 | //数据其实是一一对应的,如果没找到可能是异常了 685 | // 从第0个开始找,如果找到了,就放在usedMethod里面,并继续循环 686 | MethodFileItem *methodItem = [section.objectsList objectAtIndex:methodIndex]; 687 | if (methodItem.startAddress == startAddress) { 688 | methodItem.name = method; 689 | [obj.usedMethod setObject:methodItem forKey:method]; 690 | methodIndex ++; 691 | continue; 692 | } 693 | 694 | // 如果没找到,循环遍历查找,找到后放入,,标记下methodIndex,并继续循环 695 | for (int j = 0; j < [section.objectsList count]; j ++) { 696 | MethodFileItem *methodItem = [section.objectsList objectAtIndex:j]; 697 | if (methodItem.startAddress == startAddress) { 698 | //获取到 699 | methodIndex = j; 700 | [obj.usedMethod setObject:methodItem forKey:method]; 701 | break; 702 | } 703 | } 704 | 705 | } 706 | } 707 | 708 | 709 | 710 | //填充数据 711 | for (ObjectFileItem *obj in self.resultList) { 712 | 713 | // 将ivar的属性转为set方法,存入已使用方法表里 714 | ObjectSecionItem *allIvarSection = [obj.sectionDictionary objectForKey:@"__objc_ivar"]; 715 | for (MethodFileItem *method in allIvarSection.objectsList) { 716 | NSRange range = [method.name rangeOfString:@"." options:NSBackwardsSearch]; 717 | if (range.location == NSNotFound) { 718 | continue; 719 | } 720 | 721 | //从_开始 0x100DF2014 0x00000004 [ 16] _OBJC_IVAR_$_AFSecurityPolicy._pinnedPublicKeys 722 | //0x100DF44BC 0x00000004 [723] _OBJC_IVAR_$_HFDataBaseCore.dbPath 723 | 724 | NSString *methodStr = [method.name substringFromIndex:range.location + 1]; 725 | if ([methodStr hasPrefix:@"_"]) { 726 | methodStr = [methodStr substringFromIndex:1]; 727 | if ([methodStr length] > 1) { 728 | NSString *_methodStr = [NSString stringWithFormat:@"_%@",methodStr]; 729 | NSString *setMethod = [NSString stringWithFormat:@"set%@%@:",[methodStr substringToIndex:1].uppercaseString,[methodStr substringFromIndex:1]]; 730 | 731 | [obj.usedMethod setObject:method forKey:_methodStr]; 732 | [obj.usedMethod setObject:method forKey:setMethod]; 733 | } 734 | } 735 | [obj.usedMethod setObject:method forKey:methodStr]; 736 | 737 | } 738 | 739 | // 获取这个类使用所有的方法名称 740 | ObjectSecionItem *allClassSection = [obj.sectionDictionary objectForKey:@"__objc_methname"]; 741 | NSMutableDictionary *usedMethod = obj.usedMethod; 742 | for (MethodFileItem *method in allClassSection.objectsList) { 743 | NSString *methodStr = [method.name substringFromIndex:@"literal string: ".length]; 744 | if ([usedMethod objectForKey:methodStr]) { 745 | continue; 746 | } 747 | 748 | [obj.unUsedMethod setObject:method forKey:methodStr]; 749 | 750 | 751 | MethodFileItem *unUsedMethod = [MethodFileItem new]; 752 | unUsedMethod.size = method.size; 753 | unUsedMethod.fileTypeName = method.fileTypeName; 754 | unUsedMethod.startAddress = method.startAddress; 755 | unUsedMethod.name = [NSString stringWithFormat:@"[%@ %@]",obj.name,methodStr]; 756 | 757 | [self.unUsedSelectorList addObject:unUsedMethod]; 758 | } 759 | 760 | if ([obj.unUsedMethod count] > 0) { 761 | ObjectSecionItem *unusedSection = [[ObjectSecionItem alloc] init]; 762 | unusedSection.name = @"Z__unused_selector"; 763 | unusedSection.fileTypeName = @"Custom"; 764 | unusedSection.size = [obj.unUsedMethod count]; 765 | [unusedSection.objectsList addObjectsFromArray:[obj.unUsedMethod allValues]]; 766 | [obj.sectionDictionary setObject:unusedSection forKey:@"Z__unused_selector"]; 767 | } 768 | 769 | 770 | if ([obj.usedMethod count] > 0) { 771 | ObjectSecionItem *usedSection = [[ObjectSecionItem alloc] init]; 772 | usedSection.name = @"Z__used_selector"; 773 | usedSection.fileTypeName = @"Custom"; 774 | usedSection.size = [obj.usedMethod count]; 775 | [usedSection.objectsList addObjectsFromArray:[obj.usedMethod allValues]]; 776 | [obj.sectionDictionary setObject:usedSection forKey:@"Z__used_selector"]; 777 | } 778 | 779 | } 780 | 781 | } 782 | } 783 | 784 | - (void)anylyzeUsedClassWithData:(NSString *)string { 785 | if (string.length == 0) { 786 | return; 787 | } 788 | 789 | for (ObjectFileItem *file in self.resultList) { 790 | [file.usedClass removeAllObjects]; 791 | } 792 | 793 | NSArray *lines = [string componentsSeparatedByString:@"\n"]; 794 | NSUInteger lineNumber = 0; 795 | 796 | //计算引用到的class列表 797 | for (lineNumber = 0; lineNumber < [lines count]; lineNumber ++) { 798 | NSString *line = [lines objectAtIndex:lineNumber]; 799 | if ([line isEqualToString:@"Contents of (__DATA,__objc_classrefs) section"]) { 800 | lineNumber ++; 801 | break; 802 | } 803 | } 804 | 805 | NSString *regexStr = @"(.+?)\\s+(.+?)\\s+_OBJC_.+_\\$_(.+)"; 806 | NSRegularExpression* regexExpression = [NSRegularExpression regularExpressionWithPattern:regexStr options:NSRegularExpressionCaseInsensitive error:nil]; 807 | 808 | NSMutableDictionary *usedClassDic = [NSMutableDictionary dictionary]; 809 | 810 | 811 | int objIndex = 0; //扫描的obj的索引 812 | int methodIndex = 0; //扫描到的方法索引 813 | 814 | for (NSUInteger i = lineNumber; i < [lines count]; i++) { 815 | NSString *line = [lines objectAtIndex:i]; 816 | 817 | if ([line hasPrefix:@"contents of"]) { 818 | break; 819 | } 820 | 821 | NSArray* matchs = [regexExpression matchesInString:line options:0 range:NSMakeRange(0, line.length)]; 822 | if ([matchs count] < 1) { 823 | continue; 824 | } 825 | NSTextCheckingResult *checkingResult = [matchs objectAtIndex:0]; 826 | 827 | if ([checkingResult numberOfRanges] < 4) { 828 | continue; 829 | } 830 | 831 | NSString *startAddressStr = [line substringWithRange:[checkingResult rangeAtIndex:1]]; 832 | NSString *method = [line substringWithRange:[checkingResult rangeAtIndex:3]]; 833 | long startAddress = strtoul([startAddressStr UTF8String], 0, 16); 834 | 835 | [usedClassDic setObject:method forKey:method]; 836 | 837 | 838 | ObjectSecionItem *section = nil; 839 | ObjectFileItem *obj = nil; 840 | 841 | //需要找到对应类的起始地址 842 | while (objIndex < [self.resultList count]) { 843 | obj = [self.resultList objectAtIndex:objIndex]; 844 | section = [obj.sectionDictionary objectForKey:@"__objc_classrefs"]; 845 | MethodFileItem *method = [section.objectsList lastObject]; 846 | if (method.startAddress < startAddress) { 847 | objIndex ++; 848 | methodIndex = 0; 849 | }else { 850 | break; 851 | } 852 | } 853 | 854 | if (objIndex >= [self.resultList count]) { 855 | break; 856 | } 857 | 858 | 859 | //数据其实是一一对应的,如果没找到可能是异常了 860 | MethodFileItem *methodItem = [section.objectsList objectAtIndex:methodIndex]; 861 | if (methodItem.startAddress == startAddress) { 862 | methodItem.name = method; 863 | [obj.usedClass setObject:methodItem forKey:method]; 864 | methodIndex ++; 865 | continue; 866 | } 867 | 868 | for (int j = 0; j < [section.objectsList count]; j ++) { 869 | MethodFileItem *methodItem = [section.objectsList objectAtIndex:j]; 870 | if (methodItem.startAddress == startAddress) { 871 | //获取到 872 | methodItem.name = method; 873 | methodIndex = j; 874 | [obj.usedClass setObject:methodItem forKey:method]; 875 | break; 876 | } 877 | } 878 | } 879 | 880 | 881 | //计算下用到的protocol 882 | 883 | for (ObjectFileItem *obj in self.resultList) { 884 | 885 | ObjectSecionItem *allProtocol = [obj.sectionDictionary objectForKey:@"__objc_protolist"]; 886 | if ([allProtocol.objectsList count] == 0) { 887 | continue; 888 | } 889 | 890 | for (MethodFileItem *methodItem in allProtocol.objectsList) { 891 | //__OBJC_LABEL_PROTOCOL_$_NSObject 892 | if (![methodItem.name hasPrefix:@"__OBJC_LABEL_PROTOCOL_$_"]) { 893 | continue; 894 | } 895 | NSString *className = [methodItem.name substringFromIndex:@"__OBJC_LABEL_PROTOCOL_$_".length]; 896 | if (className.length == 0) { 897 | continue; 898 | } 899 | 900 | [obj.usedClass setObject:methodItem forKey:className]; 901 | [usedClassDic setObject:methodItem forKey:className]; 902 | } 903 | } 904 | 905 | 906 | NSMutableDictionary *unusedClass = [NSMutableDictionary dictionaryWithCapacity:[usedClassDic count]]; 907 | 908 | for (ObjectFileItem *obj in self.resultList) { 909 | 910 | ObjectSecionItem *allClassList = [obj.sectionDictionary objectForKey:@"__objc_classname"]; 911 | if ([allClassList.objectsList count] == 0) { 912 | continue; 913 | } 914 | 915 | for (MethodFileItem *methodItem in allClassList.objectsList) { 916 | //literal string: NSObject 917 | NSString *className = [methodItem.name substringFromIndex:@"literal string: ".length]; 918 | if (className.length == 0) { 919 | continue; 920 | } 921 | if (![usedClassDic objectForKey:className]) { 922 | obj.hasUesd = NO; 923 | [obj.unUsedClass setObject:methodItem forKey:className]; 924 | [unusedClass setObject:methodItem forKey:className]; 925 | } 926 | } 927 | 928 | if ([obj.usedClass count] > 0) { 929 | ObjectSecionItem *usedSection = [[ObjectSecionItem alloc] init]; 930 | usedSection.name = @"Z__used_class"; 931 | usedSection.fileTypeName = @"Custom"; 932 | usedSection.size = [obj.usedClass count]; 933 | [usedSection.objectsList addObjectsFromArray:[obj.usedClass allValues]]; 934 | [obj.sectionDictionary setObject:usedSection forKey:@"Z__used_class"]; 935 | } 936 | 937 | if ([obj.unUsedClass count] > 0) { 938 | ObjectSecionItem *unusedSection = [[ObjectSecionItem alloc] init]; 939 | unusedSection.name = @"Z__unused_class"; 940 | unusedSection.fileTypeName = @"Custom"; 941 | unusedSection.size = [obj.unUsedClass count]; 942 | [unusedSection.objectsList addObjectsFromArray:[obj.unUsedClass allValues]]; 943 | [obj.sectionDictionary setObject:unusedSection forKey:@"Z__unused_class"]; 944 | } 945 | } 946 | 947 | [self.unUsedClassList addObjectsFromArray:[unusedClass allValues]]; 948 | } 949 | 950 | 951 | 952 | @end 953 | -------------------------------------------------------------------------------- /iSee/iSee.pch: -------------------------------------------------------------------------------- 1 | // 2 | // iSee.pch 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/25. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #ifndef iSee_iSee_pch 10 | #define iSee_iSee_pch 11 | 12 | // Include any system framework and library headers here that should be included in all compilation units. 13 | // You will also need to set the Prefix Header build setting of one or more of your targets to reference this file. 14 | #include "GSCommonDefine.h" 15 | 16 | 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /iSee/inc/GSCommonDefine.h: -------------------------------------------------------------------------------- 1 | // 2 | // GSCommonDefine.h 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/25. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #ifndef iSee_GSCommonDefine_h 10 | #define iSee_GSCommonDefine_h 11 | 12 | #define OBJECT_FILE_LOG_START_FLAG @"# Object files:\n" 13 | #define OBJECT_FILE_LOG @"[1164] /Users/Yangtsing/Library/Developer/Xcode/DerivedData/BaiduNavi-dovxmjrfdlkloberhegrwmptgjdm/Build/Products/Release-iphoneos/libbaiduNaviSDK.a(CruiseCarDrawObj.o)" 14 | 15 | #define SECTION_TABLE_START_FLAG @"# Sections:\n" 16 | #define SECTION_TABLE_LOG @"0x1000061F0 0x008190E8 __TEXT __text" 17 | 18 | #define SYMBOLS_FILE_LOG_START_FLAG @"# Symbols:\n" 19 | #define SYMBOLS_FILE_LOG @"0x1000061F0 0x000000AC [ 1] +[CJSONScanner initialize]" 20 | 21 | #define SYMBOLS_FILE_LOG_DEAD_SYMBOLS_FLAG @"# Dead Stripped Symbols:\n" 22 | 23 | #define RESULTS_DONE_NOTIFICATION @"resultsDone" 24 | #define ARC_TYPE_FIOUND_NOTIFICATION @"arcTypeFound" 25 | #define ANALYZE_PROGRESS_UPDATE_NOTIFICATION @"analyzeProgressUpdate" 26 | 27 | #define ANALYZE_BEGIN_NOTIFICATION @"analyzeBegin" 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /iSee/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/11. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, const char * argv[]) { 12 | return NSApplicationMain(argc, argv); 13 | } 14 | -------------------------------------------------------------------------------- /iSee/src/BasicCategory/NSData+DDAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+DDAdditions.h 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/26. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSData (DDAdditions) 12 | 13 | - (NSRange) rangeOfData_dd:(NSData *)dataToFind; 14 | 15 | 16 | /** 17 | * Finds and returns the range of the last occurrence of a given data within the receiver. 18 | * 19 | * @param dataToFind The data to search for. This value must not be nil.Raises an NSInvalidArgumentException if dataToFind is nil. 20 | * 21 | * @return Returns An NSRange structure giving the location and length in the receiver of the first occurrence of dataToFind. 22 | */ 23 | 24 | - (NSRange) rangeOfLastData_dd:(NSData *)dataToFind; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /iSee/src/BasicCategory/NSData+DDAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+DDAdditions.m 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/26. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import "NSData+DDAdditions.h" 10 | 11 | @implementation NSData (DDAdditions) 12 | 13 | - (NSRange) rangeOfData_dd:(NSData *)dataToFind { 14 | 15 | const void * bytes = [self bytes]; 16 | NSUInteger length = [self length]; 17 | 18 | const void * searchBytes = [dataToFind bytes]; 19 | NSUInteger searchLength = [dataToFind length]; 20 | NSUInteger searchIndex = 0; 21 | 22 | NSRange foundRange = {NSNotFound, searchLength}; 23 | for (NSUInteger index = 0; index < length; index++) { 24 | if (((char *)bytes)[index] == ((char *)searchBytes)[searchIndex]) { 25 | //the current character matches 26 | if (foundRange.location == NSNotFound) { 27 | foundRange.location = index; 28 | } 29 | searchIndex++; 30 | if (searchIndex >= searchLength) { return foundRange; } 31 | } else { 32 | searchIndex = 0; 33 | foundRange.location = NSNotFound; 34 | } 35 | } 36 | return foundRange; 37 | } 38 | 39 | - (NSRange) rangeOfLastData_dd:(NSData *)dataToFind 40 | { 41 | if (!dataToFind) { 42 | @throw NSInvalidArgumentException; 43 | return NSMakeRange(NSNotFound, 0); 44 | } 45 | const void * bytes = [self bytes]; 46 | NSUInteger length = [self length]; 47 | 48 | const void * searchBytes = [dataToFind bytes]; 49 | NSUInteger searchLength = [dataToFind length]; 50 | NSInteger searchIndex = searchLength - 1; 51 | 52 | NSRange foundRange = {NSNotFound, searchLength}; 53 | for (NSInteger index = length - 1; index >= 0; index--) { 54 | if (((char *)bytes)[index] == ((char *)searchBytes)[searchIndex]) { 55 | if (foundRange.location == NSNotFound) { 56 | foundRange.location = (NSUInteger)index; 57 | } 58 | searchIndex--; 59 | if (searchIndex < 0) {//finish string match 60 | return foundRange; 61 | } 62 | }else{ 63 | searchIndex = searchLength - 1; 64 | foundRange.location = NSNotFound; 65 | } 66 | } 67 | return foundRange; 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /iSee/src/BasicCategory/NSString+Split.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Split.h 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/10/22. 6 | // Copyright © 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (Split) 12 | 13 | - (NSArray *)stringsSplitByWhiteSpace; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /iSee/src/BasicCategory/NSString+Split.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Split.m 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/10/22. 6 | // Copyright © 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import "NSString+Split.h" 10 | 11 | @implementation NSString (Split) 12 | 13 | - (NSArray *)stringsSplitByWhiteSpace 14 | { 15 | NSMutableArray <__kindof NSString*> *tmpArray = [NSMutableArray arrayWithCapacity:8]; 16 | NSString *aString = nil; 17 | NSUInteger startIndex = 0; 18 | NSUInteger endIndex = 0; 19 | char curWord = ' '; 20 | char lastWord = ' '; 21 | if (self && ![self isEqualToString:@""]) { 22 | for (int i = 0; i < [self length]; ++i) { 23 | curWord = [self characterAtIndex: i]; 24 | if (lastWord == ' ' && curWord != ' ') {//一段连续string的开始 25 | startIndex = i; 26 | lastWord = curWord; 27 | continue; 28 | } 29 | 30 | if (lastWord != ' ' && curWord == ' ') {//一段连续string的结束 31 | endIndex = i; 32 | lastWord = curWord; 33 | if (endIndex > startIndex) { 34 | NSUInteger length = endIndex - startIndex + 1; 35 | NSRange range = NSMakeRange(startIndex, length); 36 | aString = [self substringWithRange: range]; 37 | [tmpArray addObject: aString]; 38 | } 39 | continue; 40 | } 41 | 42 | if (i == ([self length] - 1)) { //到最后一个字符 43 | if (lastWord != ' ') { 44 | aString = [self substringFromIndex: startIndex]; 45 | [tmpArray addObject: aString]; 46 | } 47 | } 48 | } 49 | } 50 | 51 | return [NSArray arrayWithArray: tmpArray]; 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /iSee/src/Data_IO/FileReader/DDFileReader.h: -------------------------------------------------------------------------------- 1 | // 2 | // DDFileReader.h 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/26. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 该类原作者为: http://stackoverflow.com/users/115730/dave-delong 8 | // 9 | 10 | #import 11 | 12 | @interface DDFileReader : NSObject 13 | { 14 | NSString * filePath; 15 | 16 | NSFileHandle * fileHandle; 17 | unsigned long long currentOffset; 18 | unsigned long long totalFileLength; 19 | 20 | NSString * lineDelimiter; 21 | NSUInteger chunkSize; 22 | } 23 | 24 | @property (nonatomic, copy) NSString * lineDelimiter; 25 | @property (nonatomic) NSUInteger chunkSize; 26 | 27 | - (id) initWithFilePath:(NSString *)aPath; 28 | 29 | - (NSString *) readLine; 30 | - (NSString *) readTrimmedLine; 31 | // 已读文件长度 与 文件总长度 比值, 范围0~1间 32 | - (double ) readedFileSizeRatio; 33 | 34 | - (void) backwardOneLine; 35 | 36 | #if NS_BLOCKS_AVAILABLE 37 | - (void) enumerateLinesUsingBlock:(void(^)(NSString*, BOOL *))block; 38 | #endif 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /iSee/src/Data_IO/FileReader/DDFileReader.m: -------------------------------------------------------------------------------- 1 | // 2 | // DDFileReader.m 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/26. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import "DDFileReader.h" 10 | #import "NSData+DDAdditions.h" 11 | 12 | @implementation DDFileReader 13 | 14 | @synthesize lineDelimiter, chunkSize; 15 | 16 | - (id) initWithFilePath:(NSString *)aPath { 17 | if (self = [super init]) { 18 | fileHandle = [NSFileHandle fileHandleForReadingAtPath:aPath]; 19 | if (fileHandle == nil) { 20 | return nil; 21 | } 22 | 23 | lineDelimiter = @"\n"; 24 | currentOffset = 0ULL; // ??? 25 | chunkSize = 128; 26 | [fileHandle seekToEndOfFile]; 27 | totalFileLength = [fileHandle offsetInFile]; 28 | //we don't need to seek back, since readLine will do that. 29 | } 30 | return self; 31 | } 32 | 33 | - (void) dealloc { 34 | [fileHandle closeFile]; 35 | currentOffset = 0ULL; 36 | 37 | } 38 | 39 | - (NSString *) readLine { 40 | if (currentOffset >= totalFileLength) { return nil; } 41 | 42 | NSData * newLineData = [lineDelimiter dataUsingEncoding:NSUTF8StringEncoding]; 43 | [fileHandle seekToFileOffset:currentOffset]; 44 | NSMutableData * currentData = [[NSMutableData alloc] init]; 45 | BOOL shouldReadMore = YES; 46 | 47 | @autoreleasepool { 48 | 49 | while (shouldReadMore) { 50 | if (currentOffset >= totalFileLength) { break; } 51 | NSData * chunk = [fileHandle readDataOfLength:chunkSize]; 52 | NSRange newLineRange = [chunk rangeOfData_dd:newLineData]; 53 | if (newLineRange.location != NSNotFound) { 54 | 55 | //include the length so we can include the delimiter in the string 56 | chunk = [chunk subdataWithRange:NSMakeRange(0, newLineRange.location+[newLineData length])]; 57 | shouldReadMore = NO; 58 | } 59 | [currentData appendData:chunk]; 60 | currentOffset += [chunk length]; 61 | } 62 | } 63 | 64 | NSString * line = [[NSString alloc] initWithData:currentData encoding:NSUTF8StringEncoding]; 65 | return line; 66 | } 67 | 68 | - (NSString *) readTrimmedLine { 69 | return [[self readLine] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 70 | } 71 | 72 | - (double) readedFileSizeRatio 73 | { 74 | 75 | return (double)currentOffset / totalFileLength; 76 | } 77 | 78 | - (void) backwardOneLine 79 | { 80 | unsigned long long tempOffset = currentOffset - lineDelimiter.length; //jump over last delimiter string 81 | NSUInteger tempChunkSize = chunkSize; 82 | if (tempOffset < tempChunkSize) { 83 | //have moved to file's head 84 | tempOffset = 0; 85 | }else{ 86 | tempOffset -= tempChunkSize; 87 | } 88 | NSData * newLineData = [lineDelimiter dataUsingEncoding:NSUTF8StringEncoding]; 89 | @autoreleasepool { 90 | while (1) { 91 | [fileHandle seekToFileOffset: tempOffset]; 92 | 93 | NSData *preChunk = [fileHandle readDataOfLength: tempChunkSize]; 94 | NSRange preDelimiterRange = {NSNotFound,0}; 95 | 96 | preDelimiterRange = [preChunk rangeOfLastData_dd: newLineData]; 97 | if (preDelimiterRange.location != NSNotFound) { 98 | tempOffset += (preDelimiterRange.location + 1); 99 | //backward a line successfully. 100 | break; 101 | } 102 | if (tempOffset == 0) { // have moved to file's header 103 | break; 104 | } 105 | if (tempOffset < tempChunkSize) { 106 | tempOffset = 0; 107 | tempChunkSize = tempOffset; 108 | }else 109 | { 110 | tempOffset -= tempChunkSize; 111 | } 112 | } 113 | } 114 | 115 | currentOffset = tempOffset; 116 | } 117 | 118 | #if NS_BLOCKS_AVAILABLE 119 | - (void) enumerateLinesUsingBlock:(void(^)(NSString*, BOOL*))block { 120 | NSString * line = nil; 121 | BOOL stop = NO; 122 | while (stop == NO && (line = [self readLine])) { 123 | block(line, &stop); 124 | } 125 | } 126 | #endif 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /iSee/src/Model/ExecutableCodeItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // ExecutableCodeItem.h 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/10/22. 6 | // Copyright © 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define SEGMENT_TYPE_CODE @"__TEXT" 12 | #define SEGMENT_TYPE_DATA @"__DATA" 13 | 14 | /** 15 | * 可执行文件的段类型 16 | */ 17 | typedef enum{ 18 | CodeType_TEXT, //代码段 19 | CodeType_DATA //数据段 20 | } GSExecutableCodeType; 21 | 22 | @interface ExecutableCodeItem : NSObject 23 | 24 | @property (nonatomic, retain) NSString *name; 25 | 26 | @property (nonatomic, assign) GSExecutableCodeType segmentType; 27 | 28 | @property (nonatomic, assign) long int size; 29 | 30 | @property (nonatomic, assign) long int startAddress; 31 | 32 | @property (nonatomic, copy, readonly) NSString *segmentTypeStr; 33 | 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /iSee/src/Model/ExecutableCodeItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // ExecutableCodeItem.m 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/10/22. 6 | // Copyright © 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import "ExecutableCodeItem.h" 10 | 11 | @implementation ExecutableCodeItem 12 | 13 | - (NSString *)segmentTypeStr { 14 | switch (_segmentType) { 15 | case CodeType_TEXT: 16 | return SEGMENT_TYPE_CODE; 17 | break; 18 | case CodeType_DATA: 19 | return SEGMENT_TYPE_DATA; 20 | break; 21 | default: 22 | break; 23 | } 24 | return @""; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /iSee/src/Model/GSLinkMapParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // GSLinkMapParser.h 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/27. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "DDFileReader.h" 11 | 12 | @interface GSLinkMapParser : NSObject 13 | 14 | @property (nonatomic, weak) DDFileReader *linkMapfileReader; 15 | 16 | /** 17 | * 判断读取的一行log是否是目标文件log,段表log,符号表的开始标记 18 | * 19 | * @param aLineStr 一行log 20 | * 21 | * @return YES:是 NO:否 22 | */ 23 | - (BOOL)isSectionStartFlag:(NSString *)aLineStr; 24 | 25 | - (BOOL)isSectionEndFlag:(NSString *)aLineStr; 26 | 27 | /** 28 | * 根据linkMap 不同段的标识字符串 开始对不同section的log解析 29 | * 30 | * @param aLineStr 可能是:目标文件段, 段表段, 符号段 31 | */ 32 | - (void)startSubParserWithStartFlag:(NSString *)aLineStr; 33 | 34 | - (NSString *)lastLinkMapLineLog; 35 | 36 | - (void)outputObjectFileSize; 37 | 38 | - (void)updateAnalyzeProgress:(double)progress; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /iSee/src/Model/GSLinkMapParser.m: -------------------------------------------------------------------------------- 1 | // 2 | // GSLinkMapParser.m 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/27. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import "GSLinkMapParser.h" 10 | #import "GSCommonDefine.h" 11 | #import "DDFileReader.h" 12 | #import "ObjectFileItem.h" 13 | #import "ExecutableCodeItem.h" 14 | #import "NSString+Split.h" 15 | #import "ObjectSecionItem.h" 16 | 17 | #define SYSTEM_LIB_PATH_PREFIX @"/Applications/Xcode" 18 | #define CUSTOM_LIB_PATH_PREFIX @"/Users/" 19 | 20 | @interface GSLinkMapParser() 21 | 22 | @property (nonatomic, retain) NSString *lastLineStr; 23 | 24 | /** 25 | * 目标文件数组 26 | */ 27 | @property (nonatomic, retain) NSArray <__kindof ObjectFileItem*> *objectFileArray; 28 | 29 | /** 30 | * 可执行代码段项目 31 | */ 32 | @property (nonatomic, retain) NSArray <__kindof ExecutableCodeItem*> *executableCodeArray; 33 | 34 | @property (nonatomic, assign) NSInteger currentExecutableIndex; 35 | 36 | @end 37 | 38 | @implementation GSLinkMapParser 39 | 40 | - (id)init 41 | { 42 | self = [super init]; 43 | if (self) { 44 | 45 | } 46 | return self; 47 | } 48 | 49 | #pragma mark - getters 50 | 51 | - (NSString *)lastLinkMapLineLog 52 | { 53 | return self.lastLineStr; 54 | } 55 | 56 | #pragma mark - outer interface 57 | 58 | - (BOOL)isSectionStartFlag:(NSString *)aLineStr 59 | { 60 | BOOL ret = NO; 61 | if (aLineStr) { 62 | if ([aLineStr isEqualToString: OBJECT_FILE_LOG_START_FLAG]) { 63 | ret = YES; 64 | }else if ([aLineStr isEqualToString: SECTION_TABLE_START_FLAG]) 65 | { 66 | ret = YES; 67 | }else if ([aLineStr isEqualToString: SYMBOLS_FILE_LOG_START_FLAG]) 68 | { 69 | ret = YES; 70 | }else if ([aLineStr isEqualToString: SYMBOLS_FILE_LOG_DEAD_SYMBOLS_FLAG]) 71 | { 72 | ret = YES; 73 | } 74 | } 75 | return ret; 76 | } 77 | 78 | - (BOOL)isSectionEndFlag:(NSString *)aLineStr { 79 | return [aLineStr isEqualToString: SYMBOLS_FILE_LOG_DEAD_SYMBOLS_FLAG]; 80 | } 81 | 82 | - (void)startSubParserWithStartFlag:(NSString *)aLineStr 83 | { 84 | if ([aLineStr isEqualToString: OBJECT_FILE_LOG_START_FLAG]) { 85 | [self parseObjectFileLog]; 86 | }else if ([aLineStr isEqualToString: SECTION_TABLE_START_FLAG]) 87 | { 88 | 89 | [self parseSectionTableLog]; 90 | }else 91 | { 92 | 93 | [self parseSymbolTableLog]; 94 | } 95 | } 96 | 97 | - (void)outputObjectFileSize 98 | { 99 | [[NSNotificationCenter defaultCenter] postNotificationName: RESULTS_DONE_NOTIFICATION object:_objectFileArray]; 100 | } 101 | 102 | - (void)updateAnalyzeProgress:(double)progress 103 | { 104 | [[NSNotificationCenter defaultCenter] postNotificationName: ANALYZE_PROGRESS_UPDATE_NOTIFICATION object: @(progress)]; 105 | } 106 | 107 | #pragma mark - inner logic 108 | 109 | /** 110 | * 解析目标文件log 111 | */ 112 | - (void)parseObjectFileLog 113 | { 114 | NSMutableArray *tmpArray = [NSMutableArray arrayWithCapacity: 100]; 115 | 116 | self.lastLineStr = [_linkMapfileReader readLine]; 117 | while (![self isSectionStartFlag: _lastLineStr]) {//如果没检测到下一段不同类型log的起始标识串,则继续 118 | // NSLog(@"lastLine = %@",_lastLineStr); 119 | if ([self.lastLineStr hasPrefix:@"#"]) { 120 | self.lastLineStr = [_linkMapfileReader readLine]; 121 | continue; 122 | } 123 | 124 | NSString *regexStr = @"\\[\\s*(\\d+)\\]\\s+(.+)"; 125 | NSRegularExpression* regexExpression = [NSRegularExpression regularExpressionWithPattern:regexStr options:NSRegularExpressionCaseInsensitive error:nil]; 126 | NSArray* matchs = [regexExpression matchesInString:self.lastLineStr options:0 range:NSMakeRange(0, self.lastLineStr.length)]; 127 | 128 | if (matchs == nil || [matchs count] == 0) { 129 | return; 130 | } 131 | 132 | NSTextCheckingResult *checkingResult = [matchs objectAtIndex:0]; 133 | 134 | if ([checkingResult numberOfRanges] < 3) { 135 | return; 136 | } 137 | 138 | NSString *indexStr = [self.lastLineStr substringWithRange:[checkingResult rangeAtIndex:1]];//索引 139 | NSUInteger index = indexStr.integerValue; 140 | NSString *path = [self.lastLineStr substringWithRange:[checkingResult rangeAtIndex:2]];//索引 141 | NSRange range = [path rangeOfString:@"/"]; 142 | ObjectFileItem * objFileItem = [[ObjectFileItem alloc] init]; 143 | if (range.location == NSNotFound) { 144 | objFileItem.fileType = OBJECT_FILE_FROM_CUSTOM_CODE; 145 | objFileItem.name = path; 146 | objFileItem.module = @"Custom"; 147 | } else { 148 | NSString *pathStr = [path substringFromIndex:range.location]; 149 | NSString * objectFileName = [pathStr lastPathComponent]; 150 | // NSLog(@"path = %@, fileName= %@",pathStr,objectFileName); 151 | if ([pathStr hasPrefix: CUSTOM_LIB_PATH_PREFIX]) { 152 | NSRange bracketRange = [objectFileName rangeOfString: @"("]; 153 | if (bracketRange.location != NSNotFound ) { 154 | //静态库中的目标文件 155 | objFileItem.module = [objectFileName substringToIndex:bracketRange.location]; 156 | objFileItem.fileType = OBJECT_FILE_FROM_STATIC_FILE; 157 | NSRange objNameRange = bracketRange; 158 | objNameRange.location ++; 159 | objNameRange.length = objectFileName.length - (objNameRange.location + 1) - 1; //去掉两个括号 160 | objFileItem.name = [objectFileName substringWithRange: objNameRange]; 161 | }else 162 | { 163 | //用户自行创建的类 164 | objFileItem.fileType = OBJECT_FILE_FROM_CUSTOM_CODE; 165 | objFileItem.name = objectFileName; 166 | objFileItem.module = @"Custom"; 167 | } 168 | 169 | }else if ([pathStr hasPrefix: SYSTEM_LIB_PATH_PREFIX]) 170 | { //系统库目标文件 171 | objFileItem.fileType = OBJECT_FILE_FROM_SYSTEM_LIB; 172 | objFileItem.name = objectFileName; 173 | objFileItem.module = @"System"; 174 | } 175 | 176 | double progress = [_linkMapfileReader readedFileSizeRatio]; 177 | [self updateAnalyzeProgress: progress]; 178 | } 179 | 180 | if (tmpArray.count > index) { 181 | [tmpArray replaceObjectAtIndex:index withObject:objFileItem]; 182 | } else { 183 | [tmpArray addObject:objFileItem]; 184 | } 185 | // one loop end, start parsing next line log 186 | self.lastLineStr = [_linkMapfileReader readLine]; 187 | double progress = [_linkMapfileReader readedFileSizeRatio]; 188 | [self updateAnalyzeProgress: progress]; 189 | } 190 | 191 | self.objectFileArray = [NSArray arrayWithArray: tmpArray]; 192 | 193 | } 194 | 195 | /** 196 | * 解析段表log 197 | */ 198 | - (void)parseSectionTableLog 199 | { 200 | NSMutableArray *tmpArray = [[NSMutableArray alloc] initWithCapacity: 50]; 201 | 202 | self.lastLineStr = [_linkMapfileReader readLine]; 203 | // NSLog(@"parseSectionTableLog = %@",self.lastLineStr); 204 | while (![self isSectionStartFlag: _lastLineStr]) { 205 | if ([self.lastLineStr hasPrefix:@"#"]) { 206 | self.lastLineStr = [_linkMapfileReader readLine]; 207 | continue; 208 | } 209 | NSArray *oneLineConponents = [_lastLineStr componentsSeparatedByString:@"\t"]; 210 | NSString *address = oneLineConponents[0]; 211 | NSString *sizeStr = oneLineConponents[1]; 212 | NSString *segmentTypeStr = oneLineConponents[2]; 213 | NSString *sectionNameStr = oneLineConponents[3]; 214 | 215 | // NSLog(@"address = %@, sizeStr = %@ segmentTypeStr = %@ sectionNameStr = %@",address,sizeStr,segmentTypeStr,sectionNameStr); 216 | 217 | ExecutableCodeItem *codeItem = [[ExecutableCodeItem alloc] init]; 218 | codeItem.size = strtoul([sizeStr UTF8String], 0, 16); 219 | NSUInteger lastIndex = [sectionNameStr length] - 1;//2 是制表符 \t 的两个字符位移 220 | codeItem.name = [sectionNameStr substringToIndex: lastIndex]; 221 | codeItem.startAddress = strtoul([address UTF8String], 0, 16); 222 | 223 | if ([segmentTypeStr isEqualToString: SEGMENT_TYPE_CODE]) { 224 | codeItem.segmentType = CodeType_TEXT; 225 | }else if ([segmentTypeStr isEqualToString: SEGMENT_TYPE_DATA]) 226 | { 227 | codeItem.segmentType = CodeType_DATA; 228 | } 229 | [tmpArray addObject: codeItem]; 230 | 231 | //one loop end , start next circle 232 | self.lastLineStr = [_linkMapfileReader readLine]; 233 | [self updateAnalyzeProgress: _linkMapfileReader.readedFileSizeRatio]; 234 | } 235 | [self updateAnalyzeProgress: _linkMapfileReader.readedFileSizeRatio]; 236 | self.executableCodeArray = [NSArray arrayWithArray: tmpArray]; 237 | 238 | } 239 | 240 | /** 241 | * 解析符号表log 242 | */ 243 | - (void)parseSymbolTableLog 244 | { 245 | self.lastLineStr = [_linkMapfileReader readLine]; 246 | self.currentExecutableIndex = 0; 247 | // NSLog(@"parseSymbolTableLog = %@",self.lastLineStr); 248 | while (_lastLineStr && ![self isSectionStartFlag: _lastLineStr]) { 249 | if ([self.lastLineStr hasPrefix:@"#"]) { 250 | self.lastLineStr = [_linkMapfileReader readLine]; 251 | continue; 252 | } 253 | // NSLog(@"_lastLineStr = %@", _lastLineStr); 254 | [self parseOneLineSymbolLog: _lastLineStr]; 255 | NSString *lastLineStr = [self nextLineSymbolLog]; 256 | self.lastLineStr = lastLineStr; 257 | [self updateAnalyzeProgress: _linkMapfileReader.readedFileSizeRatio]; 258 | 259 | } 260 | [self updateAnalyzeProgress: _linkMapfileReader.readedFileSizeRatio]; 261 | } 262 | 263 | /** 264 | * 确保读出的是一条完整的符号信息 265 | */ 266 | - (NSString *)nextLineSymbolLog 267 | { 268 | NSString *symbolStr = [_linkMapfileReader readLine]; 269 | 270 | if ([self isSectionStartFlag:symbolStr]) { 271 | return symbolStr; 272 | } 273 | 274 | NSString *nextLine = [_linkMapfileReader readLine]; 275 | 276 | while (nextLine && ![nextLine hasPrefix:@"0x"] && ![self isSectionStartFlag:nextLine] && ![self isSectionStartFlag:symbolStr]) {//下一条符号信息的固定头部 277 | symbolStr = [self isSectionStartFlag:nextLine] ? symbolStr : [symbolStr stringByAppendingString: nextLine]; 278 | nextLine = [self isSectionStartFlag:nextLine] ? nextLine : [_linkMapfileReader readLine]; 279 | } 280 | 281 | if ([nextLine hasPrefix: @"0x"] || [self isSectionStartFlag:nextLine]) { 282 | //回退一行 283 | 284 | [_linkMapfileReader backwardOneLine]; 285 | } 286 | 287 | return symbolStr; 288 | } 289 | 290 | - (NSString *)nextNoNilString 291 | { 292 | NSString *symbolStr = [_linkMapfileReader readLine]; 293 | while (!symbolStr) { 294 | symbolStr = [_linkMapfileReader readLine]; 295 | } 296 | return symbolStr; 297 | } 298 | 299 | /** 300 | * 解析一行符号log 301 | * 302 | * @param oneLineLog 一行符号log 303 | * 304 | * @return 解析结果 305 | */ 306 | - (void)parseOneLineSymbolLog:(NSString *)oneLineLog 307 | { 308 | // NSLog(@"parseOneLineSymbolLog = %@", oneLineLog); 309 | //过滤非目标串 310 | NSString *filtreString = @"\t * \n * \x10\n * %@\n * \r\n"; 311 | NSRange range = [filtreString rangeOfString: oneLineLog]; 312 | if (range.location != NSNotFound) { 313 | return; 314 | } 315 | 316 | 317 | NSString *regexStr = @"(.+?)\\t(.*?)\\t\\[\\s*(\\d+)\\]\\s+(.+)"; 318 | NSRegularExpression* regexExpression = [NSRegularExpression regularExpressionWithPattern:regexStr options:NSRegularExpressionCaseInsensitive error:nil]; 319 | NSArray* matchs = [regexExpression matchesInString:oneLineLog options:0 range:NSMakeRange(0, oneLineLog.length)]; 320 | 321 | if (matchs == nil || [matchs count] == 0) { 322 | return; 323 | } 324 | 325 | NSTextCheckingResult *checkingResult = [matchs objectAtIndex:0]; 326 | 327 | if ([checkingResult numberOfRanges] < 5) { 328 | return; 329 | } 330 | 331 | 332 | NSString *startAddressStr = [oneLineLog substringWithRange:[checkingResult rangeAtIndex:1]];//起始地址 333 | NSString *sizeStr = [oneLineLog substringWithRange:[checkingResult rangeAtIndex:2]];//空间大小 334 | NSString *indexStr = [oneLineLog substringWithRange:[checkingResult rangeAtIndex:3]];//索引 335 | NSString *name = [oneLineLog substringWithRange:[checkingResult rangeAtIndex:4]];//名称 336 | 337 | long startAddress = strtoul([startAddressStr UTF8String], 0, 16); 338 | long size = strtoul([sizeStr UTF8String], 0, 16); 339 | NSUInteger index = indexStr.integerValue; 340 | 341 | ExecutableCodeItem *executable = [self excutableItem:startAddress];//段名称 342 | //添加到所属的目标文件 343 | if (index < _objectFileArray.count) { 344 | ObjectFileItem *targetObjectFile = _objectFileArray[ index ]; 345 | targetObjectFile.size += size; 346 | 347 | ObjectSecionItem *section = [targetObjectFile.sectionDictionary objectForKey:executable.name]; 348 | if (section == nil) { 349 | section = [[ObjectSecionItem alloc] init]; 350 | section.name = executable.name; 351 | section.fileTypeName = executable.segmentTypeStr; 352 | [targetObjectFile.sectionDictionary setObject:section forKey:executable.name]; 353 | } 354 | 355 | section.size += size; 356 | 357 | MethodFileItem *funcItem = [[MethodFileItem alloc] init]; 358 | funcItem.name = name; 359 | funcItem.size = size; 360 | funcItem.fileTypeName = executable.name; 361 | funcItem.startAddress = startAddress; 362 | [section.objectsList addObject:funcItem]; 363 | 364 | // NSLog(@"startAddress = %@ size = %@ index = %@ name = %@ target = %@ section = %@",startAddressStr,sizeStr,indexStr,name,targetObjectFile, executable.name); 365 | } 366 | } 367 | 368 | 369 | - (ExecutableCodeItem *)excutableItem:(long)startAddress { 370 | NSInteger index = self.currentExecutableIndex + 1; 371 | if (index >= [self.executableCodeArray count]) { 372 | index = [self.executableCodeArray count] - 1; 373 | } 374 | 375 | ExecutableCodeItem *item = [self.executableCodeArray objectAtIndex:index]; 376 | if (startAddress >= item.startAddress) { 377 | self.currentExecutableIndex ++; 378 | return item; 379 | }else { 380 | item = [self.executableCodeArray objectAtIndex:self.currentExecutableIndex]; 381 | return item; 382 | } 383 | } 384 | 385 | 386 | @end 387 | -------------------------------------------------------------------------------- /iSee/src/Model/MethodFileItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // MethodFileItem.h 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/11. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * 方法文件项目, 用于统计某一个具体的方法占用数据大小 13 | */ 14 | @interface MethodFileItem : NSObject 15 | 16 | @property (nonatomic, retain) NSString *name; 17 | 18 | @property (nonatomic, assign) long int size; 19 | 20 | @property (nonatomic, assign) NSString *fileTypeName; 21 | 22 | @property (nonatomic, assign) long int startAddress; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /iSee/src/Model/MethodFileItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // MethodFileItem.m 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/11. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import "MethodFileItem.h" 10 | 11 | @implementation MethodFileItem 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /iSee/src/Model/ModuleItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // ModuleItem.h 3 | // iSee 4 | // 5 | // Created by bolei on 2017/6/14. 6 | // Copyright © 2017年 ___zyang.Sir___. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ObjectFileItem; 12 | @interface ModuleItem : NSObject 13 | 14 | @property (nonatomic, strong) NSMutableArray *objectsList; 15 | @property (nonatomic, copy) NSString *name; 16 | @property (nonatomic, assign) long size; 17 | @property (nonatomic, assign) NSString *fileTypeName; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /iSee/src/Model/ModuleItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // ModuleItem.m 3 | // iSee 4 | // 5 | // Created by bolei on 2017/6/14. 6 | // Copyright © 2017年 ___zyang.Sir___. All rights reserved. 7 | // 8 | 9 | #import "ModuleItem.h" 10 | 11 | @implementation ModuleItem 12 | 13 | 14 | - (NSMutableArray *)objectsList { 15 | if (_objectsList == nil) { 16 | _objectsList = [NSMutableArray arrayWithCapacity:4]; 17 | } 18 | return _objectsList; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /iSee/src/Model/ObjectFileItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // ObjectFileItem.h 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/11. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MethodFileItem.h" 11 | 12 | /** 13 | * 目标文件的来源 14 | */ 15 | typedef enum{ 16 | OBJECT_FILE_FROM_INVALID_VAL = 0x00000000, //无效值 17 | OBJECT_FILE_FROM_CUSTOM_CODE = 0x00000001, //自定义可见代码 18 | OBJECT_FILE_FROM_STATIC_FILE = 0x00000010, //第三方静态库 19 | OBJECT_FILE_FROM_SYSTEM_LIB = 0x00000100 //系统标准库 20 | } OBJECT_FILE_SRC_ENUM; 21 | 22 | @class ObjectSecionItem; 23 | 24 | /** 25 | * 目标文件项目, 用于统计.o 文件 26 | */ 27 | @interface ObjectFileItem : NSObject 28 | 29 | @property (nonatomic, copy) NSString *name; 30 | @property (nonatomic, copy) NSString *module; 31 | @property (nonatomic, assign) long long size; 32 | @property (nonatomic, assign) OBJECT_FILE_SRC_ENUM fileType; 33 | @property (nonatomic, assign, readonly) NSString *fileTypeName; 34 | @property (nonatomic, assign) BOOL hasUesd; 35 | 36 | /* 保存object的段信息 37 | 38 | * __TEXT端节名含义 39 | 40 | 1. __text: 代码节,存放机器编译后的代码 41 | 2. __stubs: 用于辅助做动态链接代码(dyld). 42 | 3. __stub_helper:用于辅助做动态链接(dyld). 43 | 4. __objc_methname:objc的方法名称 44 | 5. __cstring:代码运行中包含的字符串常量,比如代码中定义`#define kGeTuiPushAESKey @"DWE2#@e2!"`,那DWE2#@e2!会存在这个区里。 45 | 6. __objc_classname:objc类名 46 | 7. __objc_methtype:objc方法类型 47 | 8. __ustring: 48 | 9. __gcc_except_tab: 49 | 10. __const:存储const修饰的常量 50 | 11. __dof_RACSignal: 51 | 12. __dof_RACCompou: 52 | 13. __unwind_info: 53 | __DATA端节名含义 54 | 55 | 1. __got:存储引用符号的实际地址,类似于动态符号表 56 | 2. __la_symbol_ptr:lazy symbol pointers。懒加载的函数指针地址。和__stubs和stub_helper配合使用。具体原理暂留。 57 | 3. __mod_init_func:模块初始化的方法。 58 | 4. __const:存储constant常量的数据。比如使用extern导出的const修饰的常量。 59 | 5. __cfstring:使用Core Foundation字符串 60 | 6. __objc_classlist:objc类列表,保存类信息,映射了__objc_data的地址 61 | 7. __objc_nlclslist:Objective-C 的 +load 函数列表,比 __mod_init_func 更早执行。 62 | 8. __objc_catlist: categories 63 | 9. __objc_nlcatlist:Objective-C 的categories的 +load函数列表。 64 | 10. __objc_protolist:objc协议列表 65 | 11. __objc_imageinfo:objc镜像信息 66 | 12. __objc_const:objc常量。保存objc_classdata结构体数据。用于映射类相关数据的地址,比如类名,方法名等。 67 | 13. __objc_selrefs:引用到的objc方法 68 | 14. __objc_protorefs:引用到的objc协议 69 | 15. __objc_classrefs:引用到的objc类 70 | 16. __objc_superrefs:objc超类引用 71 | 17. __objc_ivar:objc ivar指针,存储属性。 72 | 18. __objc_data:objc的数据。用于保存类需要的数据。最主要的内容是映射__objc_const地址,用于找到类的相关数据。 73 | 19. __data:暂时没理解,从日志看存放了协议和一些固定了地址的静态量。 74 | 20. __bss:存储未初始化的静态量。比如:`static NSThread *_networkRequestThread = nil;`其中这里面的size表示应用运行占用的内存,不是实际的占用空间。所以计算大小的时候应该去掉这部分数据。 75 | 21. __common:存储导出的全局的数据。类似于static,但是没有用static修饰。比如KSCrash里面`NSDictionary* g_registerOrders;`, g_registerOrders就存储在__common里面 76 | 77 | * 78 | */ 79 | @property (nonatomic, strong) NSMutableDictionary *sectionDictionary; 80 | 81 | @property (nonatomic, strong) NSMutableDictionary *usedMethod; //有使用过的方法,用__objc_methname和__objc_selrefs两者对比得到,其中__objc_selrefs需要用otool解析 82 | @property (nonatomic, strong) NSMutableDictionary *unUsedMethod; //未使用的方法,用__objc_methname和__objc_selrefs两者对比得到,其中__objc_selrefs需要用otool解析 83 | @property (nonatomic, strong) NSMutableDictionary *usedClass; //使用的类,用__objc_classname和__objc_classrefs两者对比得到,其中__objc_classrefs需要用otool解析 84 | 85 | @property (nonatomic, strong) NSMutableDictionary *unUsedClass; //使用的类,用__objc_classname和__objc_classrefs两者对比得到,其中__objc_classrefs需要用otool解析 86 | 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /iSee/src/Model/ObjectFileItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // ObjectFileItem.m 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/11. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import "ObjectFileItem.h" 10 | 11 | @interface ObjectFileItem() 12 | 13 | @property (nonatomic, retain) NSMutableArray<__kindof MethodFileItem*> *methodsArray; 14 | 15 | @end 16 | 17 | @implementation ObjectFileItem 18 | 19 | 20 | - (NSString *)description { 21 | return [NSString stringWithFormat:@"module = %@,name = %@,size = %@",_module,_name,@(_size)]; 22 | } 23 | 24 | - (instancetype)init { 25 | self = [super init]; 26 | if (self) { 27 | self.module = @"Custom"; 28 | self.sectionDictionary = [NSMutableDictionary dictionaryWithCapacity:20]; 29 | self.usedMethod = [NSMutableDictionary dictionaryWithCapacity:20]; 30 | self.unUsedMethod = [NSMutableDictionary dictionaryWithCapacity:20]; 31 | self.usedClass = [NSMutableDictionary dictionaryWithCapacity:20]; 32 | self.unUsedClass = [NSMutableDictionary dictionaryWithCapacity:20]; 33 | self.hasUesd = YES; 34 | } 35 | return self; 36 | } 37 | 38 | - (NSString *)fileTypeName { 39 | switch (_fileType) { 40 | case OBJECT_FILE_FROM_INVALID_VAL: 41 | return @"Invalid"; 42 | break; 43 | case OBJECT_FILE_FROM_CUSTOM_CODE: 44 | return @"Custom_Code"; 45 | break; 46 | case OBJECT_FILE_FROM_STATIC_FILE: 47 | return @"Staic_File"; 48 | break; 49 | case OBJECT_FILE_FROM_SYSTEM_LIB: 50 | return @"System_Lib"; 51 | break; 52 | default: 53 | break; 54 | } 55 | return @"UNKnow"; 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /iSee/src/Model/ObjectSecionItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // ObjectSecionItem.h 3 | // iSee 4 | // 5 | // Created by bolei on 2017/6/15. 6 | // Copyright © 2017年 ___zyang.Sir___. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class MethodFileItem; 12 | @interface ObjectSecionItem : NSObject 13 | 14 | @property (nonatomic, copy) NSString *name; 15 | @property (nonatomic, assign) long size; 16 | @property (nonatomic, assign) NSString *fileTypeName; 17 | @property (nonatomic, strong) NSMutableArray *objectsList; 18 | 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /iSee/src/Model/ObjectSecionItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // ObjectSecionItem.m 3 | // iSee 4 | // 5 | // Created by bolei on 2017/6/15. 6 | // Copyright © 2017年 ___zyang.Sir___. All rights reserved. 7 | // 8 | 9 | #import "ObjectSecionItem.h" 10 | 11 | @implementation ObjectSecionItem 12 | 13 | - (instancetype)init { 14 | self = [super init]; 15 | if (self) { 16 | self.objectsList = [NSMutableArray arrayWithCapacity:10]; 17 | } 18 | return self; 19 | } 20 | 21 | 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /iSee/src/Model/StaticFileItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // StaticFileItem.h 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/11. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * 静态库类,用于统计 .a 文件 13 | */ 14 | @interface StaticFileItem : NSObject 15 | 16 | @property (nonatomic, retain) NSString *name; 17 | 18 | @property (nonatomic, assign) long int size; 19 | 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /iSee/src/Model/StaticFileItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // StaticFileItem.m 3 | // iSee 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/11. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import "StaticFileItem.h" 10 | 11 | @implementation StaticFileItem 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /iSeeTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.baidu.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /iSeeTests/iSeeTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // iSeeTests.m 3 | // iSeeTests 4 | // 5 | // Created by Yangtsing.Zhang on 15/8/11. 6 | // Copyright (c) 2015年 ___Baidu Inc.___. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface iSeeTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation iSeeTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /iSee_detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishibolei/iSee/b8c133420f645d88b8d0ce9e17f660365d204df8/iSee_detail.png -------------------------------------------------------------------------------- /iSee_step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishibolei/iSee/b8c133420f645d88b8d0ce9e17f660365d204df8/iSee_step1.png -------------------------------------------------------------------------------- /iSee_step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishibolei/iSee/b8c133420f645d88b8d0ce9e17f660365d204df8/iSee_step2.png -------------------------------------------------------------------------------- /iSee_step3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishibolei/iSee/b8c133420f645d88b8d0ce9e17f660365d204df8/iSee_step3.png -------------------------------------------------------------------------------- /iSee_step4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishibolei/iSee/b8c133420f645d88b8d0ce9e17f660365d204df8/iSee_step4.png -------------------------------------------------------------------------------- /iSee_step5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishibolei/iSee/b8c133420f645d88b8d0ce9e17f660365d204df8/iSee_step5.png -------------------------------------------------------------------------------- /iSee_step6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishibolei/iSee/b8c133420f645d88b8d0ce9e17f660365d204df8/iSee_step6.png --------------------------------------------------------------------------------