├── gui
├── ScreenShot1.png
├── ScreenShot2.png
├── LinkMap.xcodeproj
│ ├── xcuserdata
│ │ ├── Aaron.xcuserdatad
│ │ │ ├── xcdebugger
│ │ │ │ └── Breakpoints_v2.xcbkptlist
│ │ │ └── xcschemes
│ │ │ │ ├── xcschememanagement.plist
│ │ │ │ └── LinkMap.xcscheme
│ │ └── lidahe.xcuserdatad
│ │ │ └── xcschemes
│ │ │ ├── xcschememanagement.plist
│ │ │ └── LinkMap.xcscheme
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcuserdata
│ │ │ ├── Aaron.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ │ │ └── lidahe.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── project.pbxproj
├── LinkMap
│ ├── MethodModel.m
│ ├── SymbolModel.m
│ ├── ViewController.h
│ ├── main.m
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── MethodModel.h
│ ├── SymbolModel.h
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Info.plist
│ ├── ViewController.m
│ └── Base.lproj
│ │ └── Main.storyboard
└── README.md
├── shell
├── linkmap.sh
└── linkmap.js
└── README.md
/gui/ScreenShot1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daheli/iOS-linkmap-tools/HEAD/gui/ScreenShot1.png
--------------------------------------------------------------------------------
/gui/ScreenShot2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daheli/iOS-linkmap-tools/HEAD/gui/ScreenShot2.png
--------------------------------------------------------------------------------
/gui/LinkMap.xcodeproj/xcuserdata/Aaron.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/gui/LinkMap.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/gui/LinkMap.xcodeproj/project.xcworkspace/xcuserdata/Aaron.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daheli/iOS-linkmap-tools/HEAD/gui/LinkMap.xcodeproj/project.xcworkspace/xcuserdata/Aaron.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/gui/LinkMap.xcodeproj/project.xcworkspace/xcuserdata/lidahe.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daheli/iOS-linkmap-tools/HEAD/gui/LinkMap.xcodeproj/project.xcworkspace/xcuserdata/lidahe.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/gui/LinkMap/MethodModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // MethodModel.m
3 | // LinkMap
4 | //
5 | // Created by Aaron on 16/11/2.
6 | // Copyright © 2016年 ND. All rights reserved.
7 | //
8 |
9 | #import "MethodModel.h"
10 |
11 | @implementation MethodModel
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/gui/LinkMap/SymbolModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // SymbolModel.m
3 | // LinkMap
4 | //
5 | // Created by Suteki(67111677@qq.com) on 4/8/16.
6 | // Copyright © 2016 Apple. All rights reserved.
7 | //
8 |
9 | #import "SymbolModel.h"
10 |
11 | @implementation SymbolModel
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/gui/LinkMap/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // LinkMap
4 | //
5 | // Created by Suteki(67111677@qq.com) on 4/8/16.
6 | // Copyright © 2016 Apple. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : NSViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/gui/LinkMap/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // LinkMap
4 | //
5 | // Created by Suteki(67111677@qq.com) on 4/8/16.
6 | // Copyright © 2016 Apple. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | int main(int argc, const char * argv[]) {
12 | return NSApplicationMain(argc, argv);
13 | }
14 |
--------------------------------------------------------------------------------
/gui/LinkMap/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // LinkMap
4 | //
5 | // Created by Suteki(67111677@qq.com) on 4/8/16.
6 | // Copyright © 2016 Apple. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : NSObject
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/gui/LinkMap/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // LinkMap
4 | //
5 | // Created by Suteki(67111677@qq.com) on 4/8/16.
6 | // Copyright © 2016 Apple. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/gui/LinkMap/MethodModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // MethodModel.h
3 | // LinkMap
4 | //
5 | // Created by Aaron on 16/11/2.
6 | // Copyright © 2016年 ND. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface MethodModel : NSObject
12 | @property (nonatomic, copy) NSString *methodName;
13 | @property (nonatomic, assign) NSUInteger methodSize;//大小
14 | @end
15 |
--------------------------------------------------------------------------------
/gui/LinkMap/SymbolModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // SymbolModel.h
3 | // LinkMap
4 | //
5 | // Created by Suteki(67111677@qq.com) on 4/8/16.
6 | // Copyright © 2016 Apple. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "MethodModel.h"
11 |
12 | @interface SymbolModel : NSObject
13 |
14 | @property (nonatomic, copy) NSString *file;//文件
15 | @property (nonatomic, assign) NSUInteger size;//大小
16 | @property (nonatomic, strong) NSMutableArray *methodArray;
17 | @end
18 |
--------------------------------------------------------------------------------
/gui/LinkMap.xcodeproj/xcuserdata/Aaron.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | LinkMap.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 6E7367E41CB7843200722B99
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/gui/LinkMap.xcodeproj/xcuserdata/lidahe.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | LinkMap.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 6E7367E41CB7843200722B99
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/gui/README.md:
--------------------------------------------------------------------------------
1 | # LinkMap解析工具:检查每个类占用大小
2 |
3 | ## 概述
4 |
5 | 一个大型的项目,只是代码段就有可能超过100M,算上armv7和arm64架构,就会超过200M。
6 | 这时候检查到底是哪个类、哪个第三方库占用了太多空间,就显得尤为重要。
7 |
8 | 这个工具是专为用来分析项目的LinkMap文件,得出每个类或者库所占用的空间大小(代码段+数据段),方便开发者快速定位需要优化的类或静态库。
9 |
10 |
11 | ## 使用说明
12 |
13 | 1、打开LinkMap.xcodeproj,并运行,就可以看到工具界面
14 |
15 |
16 | 2、点击“选择文件”按钮,选择LinkMap文件(如何生成LinkMap详见下方的:如何获得LinkMap文件)
17 |
18 | 3、点击“开始”按钮,就可以看到每个类/静态库所占用的空间大小
19 |
20 |
21 | 4、点击“输出文件”,可以将结果输出到文本文档中
22 |
23 |
24 | ## 如何获得LinkMap文件
25 |
26 | 1.在XCode中开启编译选项Write Link Map File \n\
27 | XCode -> Project -> Build Settings -> 把Write Link Map File选项设为yes,并指定好linkMap的存储位置
28 |
29 | 2.工程编译完成后,在编译目录里找到Link Map文件(txt类型)
30 | 默认的文件地址:~/Library/Developer/Xcode/DerivedData/XXX-xxxxxxxxxxxxx/Build/Intermediates/XXX.build/Debug-iphoneos/XXX.build/ \n\
31 |
32 |
33 | ## 联系我
34 |
35 | 如有问题或建议欢迎通过邮件联系我
36 | 67111677@qq.com
37 |
38 | ## 最后
39 |
40 | 如果喜欢,请顺手我一个star吧~ :)
--------------------------------------------------------------------------------
/gui/LinkMap/Assets.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 | }
--------------------------------------------------------------------------------
/gui/LinkMap/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
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 © 2016 Apple. All rights reserved.
29 | NSMainStoryboardFile
30 | Main
31 | NSPrincipalClass
32 | NSApplication
33 |
34 |
35 |
--------------------------------------------------------------------------------
/shell/linkmap.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #echo "" > /tmp/method.txt;
3 |
4 | # for((i=83;i<=111;i++));do
5 | # col_number=6
6 | # if [ $i -ge 100 ]; then
7 | # col_number=5
8 | # fi
9 | # cat /Users/lidahe/Library/Developer/Xcode/DerivedData/GMLibBundle-gjhxlbmiadlhyxbmtpumgajbzvzz/Build/Intermediates/Sample.build/Release-iphoneos/Sample.build/Sample-LinkMap-normal-arm64--LinkMap--.txt | grep $i']' | grep -E '(\-|\+)\[' | grep -E -v '___|.cxx_destruct' | awk '{print $'$col_number'}'
10 | # # >> /tmp/method.txt
11 | # done;
12 |
13 | LINK_PATH=$1
14 | LINK_PATH2=$2
15 | TEMP=/tmp/linkmap.txt
16 | echo 'path='$LINK_PATH
17 |
18 | echo -e "\n↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ OC ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"
19 | node ~/bin/linkmap.js $LINK_PATH -h | grep 'libGame' | grep '(GM\|Cocoa'
20 | echo -e "\n↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ C++ ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"
21 | node ~/bin/linkmap.js $LINK_PATH -h | grep 'libGame' | grep -v '(GM\|Cocoa'
22 |
23 | SIZE_1=`node ~/bin/linkmap.js $LINK_PATH | grep 'libGame' | grep '(GM\|Cocoa' | awk '{sum+=$2};END {print sum}'`
24 | SIZE_2=`node ~/bin/linkmap.js $LINK_PATH | grep 'libGame' | grep -v '(GM\|Cocoa' | awk '{sum+=$2};END {print sum}'`
25 | SIZE_0=`expr $SIZE_1 + $SIZE_2`
26 | echo -e "\ntotle:$SIZE_0 | oc:$SIZE_1 | c++:$SIZE_2"
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #静态库占用的空间大小优化
2 |
3 | ##概述
4 | 一个大型的项目,只是代码段就有可能超过100M,算上armv7和arm64架构,就会超过200M。 这时候检查到底是哪个类、哪个方法占用了太多空间,就显得尤为重要。这个汇总了一些工具及其方法.
5 |
6 |
7 | ##LinkMap文件
8 | ###如何获得LinkMap文件
9 | * 在XCode中开启编译选项Write Link Map File \n\ XCode -> Project -> Build Settings -> 把Write Link Map File选项设为yes,并指定好linkMap的存储位置
10 | * 工程编译完成后,在编译目录里找到Link Map文件(txt类型) 默认的文件地址:~/Library/Developer/Xcode/DerivedData/XXX-xxxxxxxxxxxxx/Build/Intermediates/XXX.build/Debug-iphoneos/XXX.build/
11 | 是专为用来分析项目的LinkMap文件,得出每个类或者库所占用的空间大小(代码段+数据段),方便开发者快速定位需要优化的类或静态库。
12 |
13 | ###类占用大小
14 | [linkmap.sh](https://github.com/daheli/iOS-linkmap-tools/tree/master/shell) Fork[来源](https://gist.github.com/bang590/8f3e9704f1c2661836cd)
15 |
16 | ```
17 | linkmap.sh Sample.build/Sample-LinkMap-normal-arm64--LinkMap--.txt
18 |
19 | ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ OC ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
20 | libGameMaster.a(CocoaVpn.o) 17.59KB
21 | libGameMaster.a(GMNodeList.o) 15.04KB
22 | libGameMaster.a(GMEngine.o) 14.00KB
23 | libGameMaster.a(GMVpnManager.o) 12.89KB
24 | libGameMaster.a(GMUdpConfig.o) 9.46KB
25 | .....
26 |
27 | ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ C++ ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
28 | libGameMaster.a(connector.o) 26.62KB
29 | libGameMaster.a(udp_link_create.o) 19.87KB
30 | libGameMaster.a(delay_measure.o) 18.69KB
31 | libGameMaster.a(qos_accel.o) 14.95KB
32 | libGameMaster.a(detect.o) 14.84KB
33 | ......
34 |
35 | totle:423267 | oc:161096 | c++:262171
36 | ```
37 |
38 |
39 | ###方法占用大小
40 | [检查每个类占用空间大小工具 增强版](https://github.com/daheli/iOS-linkmap-tools/tree/master/gui) Fork[来源](https://github.com/huanxsd/LinkMap)
41 | 
42 | 
43 |
44 | ###汇编占用大小
45 | ```
46 | 反汇编目标文件,查看方法使用指令情况
47 | objdump -disassemble arm64/GMGameMaster.o
48 |
49 | ```
50 | 
51 |
52 |
53 | ##架构及其设计
54 | ###减小复杂度
55 | *简化逻辑,抽取相同的代码之前的结构. 调用关系比较混乱,并且有不当的单例*
56 | 
57 |
58 | 
59 |
60 |
61 | ###分析工具Doxygen
62 | * 需要单独安装Dot
63 | * Graphviz(call graphs)
64 | 
65 |
66 |
67 | ###减少方法名称
68 | 方法名称匹配替换成4-8位字符串,整体会减少1k左右.
69 |
70 | [详细使用方式](https://github.com/wg689/Security-And-CodeConfuse)
71 |
72 | ```
73 | 但是弊处还是很明显的, 例如:
74 | * 每次编译都有留存方法名称映射
75 | * 某些关键词和系统冲突,并且编译时不会报错
76 | ```
77 |
78 |
79 | ##OC代码
80 |
81 | ```
82 | @property -> 实例变量
83 | 因为属性的语法编译器会做getter/setter等额外工作
84 | ```
85 |
86 | ```
87 | 减少if/switch判断
88 | ```
89 |
90 | ```
91 | 变量减少声明
92 | Object myObj = ...;
93 | for(;;) {
94 | myObj = ...;
95 | myObj.doSomething();
96 | }
97 | ```
98 |
99 | ```
100 | 相同代码片段大于5行,进行提取抽象
101 | ```
102 |
103 | ```
104 | 缩短字符串内容
105 | 例如:
106 | NSString *text = @"越少越好"
107 | ```
108 |
109 |
110 |
111 |
112 | ##CPP代码
113 | ```
114 | 裁剪功能
115 | 优化编译参数 -Os
116 | 禁用内联、宏替转成函数
117 | 抽取功能子模块
118 | 减少全局变量
119 | ```
120 |
121 |
122 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/gui/LinkMap.xcodeproj/xcuserdata/Aaron.xcuserdatad/xcschemes/LinkMap.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/gui/LinkMap.xcodeproj/xcuserdata/lidahe.xcuserdatad/xcschemes/LinkMap.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/shell/linkmap.js:
--------------------------------------------------------------------------------
1 | var readline = require('readline'),
2 | fs = require('fs');
3 |
4 | var LinkMap = function(filePath) {
5 | this.files = []
6 | this.filePath = filePath
7 | }
8 |
9 | LinkMap.prototype = {
10 | start: function(cb) {
11 | var self = this
12 | var rl = readline.createInterface({
13 | input: fs.createReadStream(self.filePath),
14 | output: process.stdout,
15 | terminal: false
16 | });
17 | var currParser = "";
18 | rl.on('line', function(line) {
19 | if (line[0] == '#') {
20 | if (line.indexOf('Object files') > -1) {
21 | currParser = "_parseFiles";
22 | } else if (line.indexOf('Sections') > -1) {
23 | currParser = "_parseSection";
24 | } else if (line.indexOf('Symbols') > -1) {
25 | currParser = "_parseSymbols";
26 | }
27 | return;
28 | }
29 | if (self[currParser]) {
30 | self[currParser](line)
31 | }
32 | });
33 |
34 | rl.on('close', function(line) {
35 | cb(self)
36 | });
37 | },
38 |
39 | _parseFiles: function(line) {
40 | var arr =line.split(']')
41 | if (arr.length > 1) {
42 | var idx = Number(arr[0].replace('[',''));
43 | var file = arr[1].split('/').pop().trim()
44 | this.files[idx] = {
45 | name: file,
46 | size: 0
47 | }
48 | }
49 | },
50 |
51 | _parseSection: function(line) {
52 | },
53 |
54 | _parseSymbols: function(line) {
55 | var arr = line.split('\t')
56 | if (arr.length > 2) {
57 | var size = parseInt(arr[1], 16)
58 | var idx = Number(arr[2].split(']')[0].replace('[', ''))
59 | if (idx && this.files[idx]) {
60 | this.files[idx].size += size;
61 | }
62 | }
63 | },
64 |
65 | _formatSize: function(size) {
66 | if (size > 1024 * 1024) return (size/(1024*1024)).toFixed(2) + "MB"
67 | if (size > 1024) return (size/1024).toFixed(2) + "KB"
68 | return size + "B"
69 | },
70 |
71 | statLibs: function(h) {
72 | var libs = {}
73 | var files = this.files;
74 | var self = this;
75 | for (var i in files) {
76 | var file = files[i]
77 | var libName
78 | if (file.name.indexOf('.o)') > -1) {
79 | libName = file.name.split('(')[0]
80 | } else {
81 | libName = file.name
82 | }
83 | if (!libs[libName]) {
84 | libs[libName] = 0
85 | }
86 | libs[libName] += file.size
87 | }
88 | var i = 0, sortLibs = []
89 | for (var name in libs) {
90 | sortLibs[i++] = {
91 | name: name,
92 | size: libs[name]
93 | }
94 | }
95 | sortLibs.sort(function(a,b) {
96 | return a.size > b.size ? -1: 1
97 | })
98 | if (h) {
99 | sortLibs.map(function(o) {
100 | o.size = self._formatSize(o.size)
101 | })
102 | }
103 | return sortLibs
104 | },
105 |
106 | statFiles: function(h) {
107 | var self = this
108 | self.files.sort(function(a,b) {
109 | return a.size > b.size ? -1: 1
110 | })
111 | if (h) {
112 | self.files.map(function(o) {
113 | o.size = self._formatSize(o.size)
114 | })
115 | }
116 | return this.files
117 | }
118 | }
119 |
120 | if (!process.argv[2]) {
121 | console.log('usage: node linkmap.js filepath -hl')
122 | console.log('-h: format size')
123 | console.log('-l: stat libs')
124 | return
125 | }
126 | var isStatLib, isFomatSize
127 | var opts = process.argv[3];
128 | if (opts && opts[0] == '-') {
129 | if (opts.indexOf('h') > -1) isFomatSize = true
130 | if (opts.indexOf('l') > -1) isStatLib = true
131 | }
132 |
133 | var linkmap = new LinkMap(process.argv[2])
134 | linkmap.start(function(){
135 | var ret = isStatLib ? linkmap.statLibs(isFomatSize)
136 | : linkmap.statFiles(isFomatSize)
137 | for (var i in ret) {
138 | console.log(ret[i].name + '\t' + ret[i].size)
139 | }
140 | })
--------------------------------------------------------------------------------
/gui/LinkMap.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 6E7367EA1CB7843200722B99 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E7367E91CB7843200722B99 /* AppDelegate.m */; };
11 | 6E7367ED1CB7843200722B99 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E7367EC1CB7843200722B99 /* main.m */; };
12 | 6E7367F01CB7843200722B99 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E7367EF1CB7843200722B99 /* ViewController.m */; };
13 | 6E7367F21CB7843200722B99 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6E7367F11CB7843200722B99 /* Assets.xcassets */; };
14 | 6E7367F51CB7843200722B99 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6E7367F31CB7843200722B99 /* Main.storyboard */; };
15 | 6E73680C1CB784F100722B99 /* SymbolModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E73680B1CB784F100722B99 /* SymbolModel.m */; };
16 | 9393DE9E1DC9C6D300DF4866 /* MethodModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 9393DE9D1DC9C6D300DF4866 /* MethodModel.m */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXFileReference section */
20 | 6E7367E51CB7843200722B99 /* LinkMap.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LinkMap.app; sourceTree = BUILT_PRODUCTS_DIR; };
21 | 6E7367E81CB7843200722B99 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
22 | 6E7367E91CB7843200722B99 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
23 | 6E7367EC1CB7843200722B99 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
24 | 6E7367EE1CB7843200722B99 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
25 | 6E7367EF1CB7843200722B99 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
26 | 6E7367F11CB7843200722B99 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
27 | 6E7367F41CB7843200722B99 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
28 | 6E7367F61CB7843200722B99 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
29 | 6E73680A1CB784F100722B99 /* SymbolModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SymbolModel.h; sourceTree = ""; };
30 | 6E73680B1CB784F100722B99 /* SymbolModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SymbolModel.m; sourceTree = ""; };
31 | 9393DE9C1DC9C6D300DF4866 /* MethodModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MethodModel.h; sourceTree = ""; };
32 | 9393DE9D1DC9C6D300DF4866 /* MethodModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MethodModel.m; sourceTree = ""; };
33 | /* End PBXFileReference section */
34 |
35 | /* Begin PBXFrameworksBuildPhase section */
36 | 6E7367E21CB7843200722B99 /* Frameworks */ = {
37 | isa = PBXFrameworksBuildPhase;
38 | buildActionMask = 2147483647;
39 | files = (
40 | );
41 | runOnlyForDeploymentPostprocessing = 0;
42 | };
43 | /* End PBXFrameworksBuildPhase section */
44 |
45 | /* Begin PBXGroup section */
46 | 6E7367DC1CB7843200722B99 = {
47 | isa = PBXGroup;
48 | children = (
49 | 6E7367E71CB7843200722B99 /* LinkMap */,
50 | 6E7367E61CB7843200722B99 /* Products */,
51 | );
52 | sourceTree = "";
53 | };
54 | 6E7367E61CB7843200722B99 /* Products */ = {
55 | isa = PBXGroup;
56 | children = (
57 | 6E7367E51CB7843200722B99 /* LinkMap.app */,
58 | );
59 | name = Products;
60 | sourceTree = "";
61 | };
62 | 6E7367E71CB7843200722B99 /* LinkMap */ = {
63 | isa = PBXGroup;
64 | children = (
65 | 6E7367E81CB7843200722B99 /* AppDelegate.h */,
66 | 6E7367E91CB7843200722B99 /* AppDelegate.m */,
67 | 6E7367EE1CB7843200722B99 /* ViewController.h */,
68 | 6E7367EF1CB7843200722B99 /* ViewController.m */,
69 | 6E73680A1CB784F100722B99 /* SymbolModel.h */,
70 | 6E73680B1CB784F100722B99 /* SymbolModel.m */,
71 | 6E7367F11CB7843200722B99 /* Assets.xcassets */,
72 | 6E7367F31CB7843200722B99 /* Main.storyboard */,
73 | 6E7367F61CB7843200722B99 /* Info.plist */,
74 | 6E7367EB1CB7843200722B99 /* Supporting Files */,
75 | 9393DE9C1DC9C6D300DF4866 /* MethodModel.h */,
76 | 9393DE9D1DC9C6D300DF4866 /* MethodModel.m */,
77 | );
78 | path = LinkMap;
79 | sourceTree = "";
80 | };
81 | 6E7367EB1CB7843200722B99 /* Supporting Files */ = {
82 | isa = PBXGroup;
83 | children = (
84 | 6E7367EC1CB7843200722B99 /* main.m */,
85 | );
86 | name = "Supporting Files";
87 | sourceTree = "";
88 | };
89 | /* End PBXGroup section */
90 |
91 | /* Begin PBXNativeTarget section */
92 | 6E7367E41CB7843200722B99 /* LinkMap */ = {
93 | isa = PBXNativeTarget;
94 | buildConfigurationList = 6E7368041CB7843200722B99 /* Build configuration list for PBXNativeTarget "LinkMap" */;
95 | buildPhases = (
96 | 6E7367E11CB7843200722B99 /* Sources */,
97 | 6E7367E21CB7843200722B99 /* Frameworks */,
98 | 6E7367E31CB7843200722B99 /* Resources */,
99 | );
100 | buildRules = (
101 | );
102 | dependencies = (
103 | );
104 | name = LinkMap;
105 | productName = LinkMap;
106 | productReference = 6E7367E51CB7843200722B99 /* LinkMap.app */;
107 | productType = "com.apple.product-type.application";
108 | };
109 | /* End PBXNativeTarget section */
110 |
111 | /* Begin PBXProject section */
112 | 6E7367DD1CB7843200722B99 /* Project object */ = {
113 | isa = PBXProject;
114 | attributes = {
115 | LastUpgradeCheck = 0720;
116 | ORGANIZATIONNAME = ND;
117 | TargetAttributes = {
118 | 6E7367E41CB7843200722B99 = {
119 | CreatedOnToolsVersion = 7.2.1;
120 | };
121 | };
122 | };
123 | buildConfigurationList = 6E7367E01CB7843200722B99 /* Build configuration list for PBXProject "LinkMap" */;
124 | compatibilityVersion = "Xcode 3.2";
125 | developmentRegion = English;
126 | hasScannedForEncodings = 0;
127 | knownRegions = (
128 | en,
129 | Base,
130 | );
131 | mainGroup = 6E7367DC1CB7843200722B99;
132 | productRefGroup = 6E7367E61CB7843200722B99 /* Products */;
133 | projectDirPath = "";
134 | projectRoot = "";
135 | targets = (
136 | 6E7367E41CB7843200722B99 /* LinkMap */,
137 | );
138 | };
139 | /* End PBXProject section */
140 |
141 | /* Begin PBXResourcesBuildPhase section */
142 | 6E7367E31CB7843200722B99 /* Resources */ = {
143 | isa = PBXResourcesBuildPhase;
144 | buildActionMask = 2147483647;
145 | files = (
146 | 6E7367F21CB7843200722B99 /* Assets.xcassets in Resources */,
147 | 6E7367F51CB7843200722B99 /* Main.storyboard in Resources */,
148 | );
149 | runOnlyForDeploymentPostprocessing = 0;
150 | };
151 | /* End PBXResourcesBuildPhase section */
152 |
153 | /* Begin PBXSourcesBuildPhase section */
154 | 6E7367E11CB7843200722B99 /* Sources */ = {
155 | isa = PBXSourcesBuildPhase;
156 | buildActionMask = 2147483647;
157 | files = (
158 | 9393DE9E1DC9C6D300DF4866 /* MethodModel.m in Sources */,
159 | 6E7367F01CB7843200722B99 /* ViewController.m in Sources */,
160 | 6E7367ED1CB7843200722B99 /* main.m in Sources */,
161 | 6E7367EA1CB7843200722B99 /* AppDelegate.m in Sources */,
162 | 6E73680C1CB784F100722B99 /* SymbolModel.m in Sources */,
163 | );
164 | runOnlyForDeploymentPostprocessing = 0;
165 | };
166 | /* End PBXSourcesBuildPhase section */
167 |
168 | /* Begin PBXVariantGroup section */
169 | 6E7367F31CB7843200722B99 /* Main.storyboard */ = {
170 | isa = PBXVariantGroup;
171 | children = (
172 | 6E7367F41CB7843200722B99 /* Base */,
173 | );
174 | name = Main.storyboard;
175 | sourceTree = "";
176 | };
177 | /* End PBXVariantGroup section */
178 |
179 | /* Begin XCBuildConfiguration section */
180 | 6E7368021CB7843200722B99 /* Debug */ = {
181 | isa = XCBuildConfiguration;
182 | buildSettings = {
183 | ALWAYS_SEARCH_USER_PATHS = NO;
184 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
185 | CLANG_CXX_LIBRARY = "libc++";
186 | CLANG_ENABLE_MODULES = YES;
187 | CLANG_ENABLE_OBJC_ARC = YES;
188 | CLANG_WARN_BOOL_CONVERSION = YES;
189 | CLANG_WARN_CONSTANT_CONVERSION = YES;
190 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
191 | CLANG_WARN_EMPTY_BODY = YES;
192 | CLANG_WARN_ENUM_CONVERSION = YES;
193 | CLANG_WARN_INT_CONVERSION = YES;
194 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
195 | CLANG_WARN_UNREACHABLE_CODE = YES;
196 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
197 | CODE_SIGN_IDENTITY = "-";
198 | COPY_PHASE_STRIP = NO;
199 | DEBUG_INFORMATION_FORMAT = dwarf;
200 | ENABLE_STRICT_OBJC_MSGSEND = YES;
201 | ENABLE_TESTABILITY = YES;
202 | GCC_C_LANGUAGE_STANDARD = gnu99;
203 | GCC_DYNAMIC_NO_PIC = NO;
204 | GCC_NO_COMMON_BLOCKS = YES;
205 | GCC_OPTIMIZATION_LEVEL = 0;
206 | GCC_PREPROCESSOR_DEFINITIONS = (
207 | "DEBUG=1",
208 | "$(inherited)",
209 | );
210 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
211 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
212 | GCC_WARN_UNDECLARED_SELECTOR = YES;
213 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
214 | GCC_WARN_UNUSED_FUNCTION = YES;
215 | GCC_WARN_UNUSED_VARIABLE = YES;
216 | MACOSX_DEPLOYMENT_TARGET = 10.11;
217 | MTL_ENABLE_DEBUG_INFO = YES;
218 | ONLY_ACTIVE_ARCH = YES;
219 | SDKROOT = macosx;
220 | };
221 | name = Debug;
222 | };
223 | 6E7368031CB7843200722B99 /* Release */ = {
224 | isa = XCBuildConfiguration;
225 | buildSettings = {
226 | ALWAYS_SEARCH_USER_PATHS = NO;
227 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
228 | CLANG_CXX_LIBRARY = "libc++";
229 | CLANG_ENABLE_MODULES = YES;
230 | CLANG_ENABLE_OBJC_ARC = YES;
231 | CLANG_WARN_BOOL_CONVERSION = YES;
232 | CLANG_WARN_CONSTANT_CONVERSION = YES;
233 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
234 | CLANG_WARN_EMPTY_BODY = YES;
235 | CLANG_WARN_ENUM_CONVERSION = YES;
236 | CLANG_WARN_INT_CONVERSION = YES;
237 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
238 | CLANG_WARN_UNREACHABLE_CODE = YES;
239 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
240 | CODE_SIGN_IDENTITY = "-";
241 | COPY_PHASE_STRIP = NO;
242 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
243 | ENABLE_NS_ASSERTIONS = NO;
244 | ENABLE_STRICT_OBJC_MSGSEND = YES;
245 | GCC_C_LANGUAGE_STANDARD = gnu99;
246 | GCC_NO_COMMON_BLOCKS = YES;
247 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
248 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
249 | GCC_WARN_UNDECLARED_SELECTOR = YES;
250 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
251 | GCC_WARN_UNUSED_FUNCTION = YES;
252 | GCC_WARN_UNUSED_VARIABLE = YES;
253 | MACOSX_DEPLOYMENT_TARGET = 10.11;
254 | MTL_ENABLE_DEBUG_INFO = NO;
255 | SDKROOT = macosx;
256 | };
257 | name = Release;
258 | };
259 | 6E7368051CB7843200722B99 /* Debug */ = {
260 | isa = XCBuildConfiguration;
261 | buildSettings = {
262 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
263 | COMBINE_HIDPI_IMAGES = YES;
264 | INFOPLIST_FILE = LinkMap/Info.plist;
265 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
266 | MACOSX_DEPLOYMENT_TARGET = 10.10;
267 | PRODUCT_BUNDLE_IDENTIFIER = com.nd.LinkMap;
268 | PRODUCT_NAME = "$(TARGET_NAME)";
269 | };
270 | name = Debug;
271 | };
272 | 6E7368061CB7843200722B99 /* Release */ = {
273 | isa = XCBuildConfiguration;
274 | buildSettings = {
275 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
276 | COMBINE_HIDPI_IMAGES = YES;
277 | INFOPLIST_FILE = LinkMap/Info.plist;
278 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
279 | MACOSX_DEPLOYMENT_TARGET = 10.10;
280 | PRODUCT_BUNDLE_IDENTIFIER = com.nd.LinkMap;
281 | PRODUCT_NAME = "$(TARGET_NAME)";
282 | };
283 | name = Release;
284 | };
285 | /* End XCBuildConfiguration section */
286 |
287 | /* Begin XCConfigurationList section */
288 | 6E7367E01CB7843200722B99 /* Build configuration list for PBXProject "LinkMap" */ = {
289 | isa = XCConfigurationList;
290 | buildConfigurations = (
291 | 6E7368021CB7843200722B99 /* Debug */,
292 | 6E7368031CB7843200722B99 /* Release */,
293 | );
294 | defaultConfigurationIsVisible = 0;
295 | defaultConfigurationName = Release;
296 | };
297 | 6E7368041CB7843200722B99 /* Build configuration list for PBXNativeTarget "LinkMap" */ = {
298 | isa = XCConfigurationList;
299 | buildConfigurations = (
300 | 6E7368051CB7843200722B99 /* Debug */,
301 | 6E7368061CB7843200722B99 /* Release */,
302 | );
303 | defaultConfigurationIsVisible = 0;
304 | defaultConfigurationName = Release;
305 | };
306 | /* End XCConfigurationList section */
307 | };
308 | rootObject = 6E7367DD1CB7843200722B99 /* Project object */;
309 | }
310 |
--------------------------------------------------------------------------------
/gui/LinkMap/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // LinkMap
4 | //
5 | // Created by Suteki(67111677@qq.com) on 4/8/16.
6 | // Copyright © 2016 Apple. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "SymbolModel.h"
11 | #import "MethodModel.h"
12 |
13 | @interface ViewController()
14 |
15 | @property (weak) IBOutlet NSTextField *filePathField;//显示选择的文件路径
16 | @property (weak) IBOutlet NSProgressIndicator *indicator;//指示器
17 | @property (weak) IBOutlet NSTextField *searchField;
18 |
19 | @property (weak) IBOutlet NSScrollView *contentView;//分析的内容
20 | @property (unsafe_unretained) IBOutlet NSTextView *contentTextView;
21 | @property (weak) IBOutlet NSButton *groupButton;
22 |
23 |
24 | @property (strong) NSURL *linkMapFileURL;
25 | @property (strong) NSString *linkMapContent;
26 |
27 | @property (strong) NSMutableString *result;//分析的结果
28 |
29 |
30 | @property (nonatomic, strong)NSMutableArray *methodArray;
31 |
32 | @end
33 |
34 | @implementation ViewController
35 |
36 | - (void)viewDidLoad {
37 | [super viewDidLoad];
38 | self.indicator.hidden = YES;
39 | self.methodArray = [NSMutableArray new];
40 | _contentTextView.editable = NO;
41 |
42 | _contentTextView.string = @"使用方式:\n\
43 | 1.在XCode中开启编译选项Write Link Map File \n\
44 | XCode -> Project -> Build Settings -> 把Write Link Map File选项设为yes,并指定好linkMap的存储位置 \n\
45 | 2.工程编译完成后,在编译目录里找到Link Map文件(txt类型) \n\
46 | 默认的文件地址:~/Library/Developer/Xcode/DerivedData/XXX-xxxxxxxxxxxxx/Build/Intermediates/XXX.build/Debug-iphoneos/XXX.build/ \n\
47 | 3.回到本应用,点击“选择文件”,打开Link Map文件 \n\
48 | 4.点击“开始”,解析Link Map文件 \n\
49 | 5.点击“输出文件”,得到解析后的Link Map文件 \n\
50 | 6. * 输入目标文件的关键字(例如:libIM),然后点击“开始”。实现搜索功能 \n\
51 | 7. * 勾选“分组解析”,然后点击“开始”。实现对不同库的目标文件进行分组";
52 | }
53 |
54 | - (IBAction)chooseFile:(id)sender {
55 | NSOpenPanel *panel = [NSOpenPanel openPanel];
56 | panel.allowsMultipleSelection = NO;
57 | panel.canChooseDirectories = NO;
58 | panel.resolvesAliases = NO;
59 | panel.canChooseFiles = YES;
60 |
61 | [panel beginWithCompletionHandler:^(NSInteger result){
62 | if (result == NSFileHandlingPanelOKButton) {
63 | NSURL *document = [[panel URLs] objectAtIndex:0];
64 | _filePathField.stringValue = document.path;
65 | self.linkMapFileURL = document;
66 | }
67 | }];
68 | }
69 |
70 | - (IBAction)analyze:(id)sender {
71 | if (!_linkMapFileURL || ![[NSFileManager defaultManager] fileExistsAtPath:[_linkMapFileURL path] isDirectory:nil]) {
72 | [self showAlertWithText:@"请选择正确的Link Map文件路径"];
73 | return;
74 | }
75 |
76 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
77 | NSString *content = [NSString stringWithContentsOfURL:_linkMapFileURL encoding:NSMacOSRomanStringEncoding error:nil];
78 |
79 | if (![self checkContent:content]) {
80 | dispatch_async(dispatch_get_main_queue(), ^{
81 | [self showAlertWithText:@"Link Map文件格式有误"];
82 | });
83 | return ;
84 | }
85 |
86 | dispatch_async(dispatch_get_main_queue(), ^{
87 | self.indicator.hidden = NO;
88 | [self.indicator startAnimation:self];
89 |
90 | });
91 |
92 | NSDictionary *symbolMap = [self symbolMapFromContent:content];
93 |
94 | NSArray *symbols = [symbolMap allValues];
95 |
96 | NSArray *sortedSymbols = [self sortSymbols:symbols];
97 |
98 | if (_groupButton.state == 1) {
99 | [self buildCombinationResultWithSymbols:sortedSymbols];
100 | } else {
101 | [self buildResultWithSymbols:sortedSymbols];
102 | }
103 |
104 | dispatch_async(dispatch_get_main_queue(), ^{
105 | self.contentTextView.string = _result;
106 | self.indicator.hidden = YES;
107 | [self.indicator stopAnimation:self];
108 |
109 | });
110 | });
111 | }
112 |
113 | - (NSMutableDictionary *)symbolMapFromContent:(NSString *)content {
114 | NSMutableDictionary *symbolMap = [NSMutableDictionary new];
115 | // 符号文件列表
116 | NSArray *lines = [content componentsSeparatedByString:@"\n"];
117 |
118 | BOOL reachFiles = NO;
119 | BOOL reachSymbols = NO;
120 | BOOL reachSections = NO;
121 |
122 | for(NSString *line in lines) {
123 | if([line hasPrefix:@"#"]) {
124 | if([line hasPrefix:@"# Object files:"])
125 | reachFiles = YES;
126 | else if ([line hasPrefix:@"# Sections:"])
127 | reachSections = YES;
128 | else if ([line hasPrefix:@"# Symbols:"])
129 | reachSymbols = YES;
130 | } else {
131 | if(reachFiles == YES && reachSections == NO && reachSymbols == NO) {
132 | NSRange range = [line rangeOfString:@"]"];
133 | if(range.location != NSNotFound) {
134 | SymbolModel *symbol = [SymbolModel new];
135 | symbol.methodArray = [NSMutableArray new];
136 | symbol.file = [line substringFromIndex:range.location+1];
137 | NSString *key = [line substringToIndex:range.location+1];
138 | symbolMap[key] = symbol;
139 | }
140 | } else if (reachFiles == YES && reachSections == YES && reachSymbols == YES) {
141 | NSArray *symbolsArray = [line componentsSeparatedByString:@"\t"];
142 | if(symbolsArray.count == 3) {
143 | NSString *fileKeyAndName = symbolsArray[2];
144 | NSUInteger size = strtoul([symbolsArray[1] UTF8String], nil, 16);
145 |
146 | NSRange range = [fileKeyAndName rangeOfString:@"]"];
147 | if(range.location != NSNotFound) {
148 | NSString *key = [fileKeyAndName substringToIndex:range.location+1];
149 | SymbolModel *symbol = symbolMap[key];
150 | MethodModel *model = [MethodModel new];
151 | model.methodName = fileKeyAndName;
152 | model.methodSize = size;
153 |
154 |
155 |
156 | [symbol.methodArray addObject:model];
157 |
158 | // [self.methodArray addObject:model];_methodArray __NSArrayM * @"0 elements" 0x000060800044ec70
159 | if(symbol) {
160 | symbol.size += size;
161 | }
162 | }
163 | }
164 | }
165 | }
166 | }
167 | return symbolMap;
168 | }
169 |
170 | - (NSArray *)sortSymbols:(NSArray *)symbols {
171 | NSArray *sortedSymbols = [symbols sortedArrayUsingComparator:^NSComparisonResult(SymbolModel * _Nonnull obj1, SymbolModel * _Nonnull obj2) {
172 | if(obj1.size > obj2.size) {
173 | return NSOrderedAscending;
174 | } else if (obj1.size < obj2.size) {
175 | return NSOrderedDescending;
176 | } else {
177 | return NSOrderedSame;
178 | }
179 | }];
180 |
181 | return sortedSymbols;
182 | }
183 |
184 | - (void)buildResultWithSymbols:(NSArray *)symbols {
185 | self.result = [@"文件大小\t文件名称\r\n\r\n" mutableCopy];
186 | NSUInteger totalSize = 0;
187 |
188 | NSString *searchKey = _searchField.stringValue;
189 |
190 |
191 | NSMutableArray *symbolArray = [NSMutableArray new];
192 | for(SymbolModel *symbol in symbols) {
193 | if (searchKey.length > 0) {
194 | if ([symbol.file containsString:searchKey]) {
195 | [symbolArray addObject:symbol];
196 | }
197 | }
198 | }
199 |
200 |
201 | // NSInteger startIndex = 0;
202 | // NSInteger endIndex = 0;
203 | // NSInteger index = 0;
204 | for(SymbolModel *symbol in symbolArray) {
205 | if (searchKey.length > 0) {
206 | if ([symbol.file containsString:searchKey]) {
207 | // if (startIndex == 0) {
208 | //// startIndex = index;
209 | // NSLog(@"startIndex = %ld",(long)startIndex);
210 | // }
211 | [self appendResultWithSymbol:symbol];
212 | totalSize += symbol.size;
213 | // endIndex = index;
214 | }
215 | } else {
216 | [self appendResultWithSymbol:symbol];
217 | totalSize += symbol.size;
218 | }
219 | // ++index;
220 | }
221 |
222 | [_result appendFormat:@"\r\n总大小: %.2fM\r\n",(totalSize/1024.0/1024.0)];
223 | }
224 |
225 |
226 | - (void)buildCombinationResultWithSymbols:(NSArray *)symbols {
227 | self.result = [@"库大小\t库名称\r\n\r\n" mutableCopy];
228 | NSUInteger totalSize = 0;
229 |
230 | NSMutableDictionary *combinationMap = [[NSMutableDictionary alloc] init];
231 |
232 | for(SymbolModel *symbol in symbols) {
233 | NSString *name = [[symbol.file componentsSeparatedByString:@"/"] lastObject];
234 | if ([name hasSuffix:@")"] &&
235 | [name containsString:@"("]) {
236 | NSRange range = [name rangeOfString:@"("];
237 | NSString *component = [name substringToIndex:range.location];
238 |
239 | SymbolModel *combinationSymbol = [combinationMap objectForKey:component];
240 | if (!combinationSymbol) {
241 | combinationSymbol = [[SymbolModel alloc] init];
242 | [combinationMap setObject:combinationSymbol forKey:component];
243 | }
244 |
245 | combinationSymbol.size += symbol.size;
246 | combinationSymbol.file = component;
247 | } else {
248 | // symbol可能来自app本身的目标文件或者系统的动态库,在最后的结果中一起显示
249 | [combinationMap setObject:symbol forKey:symbol.file];
250 | }
251 | }
252 |
253 | NSArray *combinationSymbols = [combinationMap allValues];
254 |
255 | NSArray *sortedSymbols = [self sortSymbols:combinationSymbols];
256 |
257 | NSString *searchKey = _searchField.stringValue;
258 |
259 | for(SymbolModel *symbol in sortedSymbols) {
260 | if (searchKey.length > 0) {
261 | if ([symbol.file containsString:searchKey]) {
262 | [self appendResultWithSymbol:symbol];
263 | totalSize += symbol.size;
264 | }
265 | } else {
266 | [self appendResultWithSymbol:symbol];
267 | totalSize += symbol.size;
268 | }
269 | }
270 |
271 | [_result appendFormat:@"\r\n总大小: %.2fM\r\n",(totalSize/1024.0/1024.0)];
272 | }
273 |
274 | - (IBAction)ouputFile:(id)sender {
275 | NSOpenPanel* panel = [NSOpenPanel openPanel];
276 | [panel setAllowsMultipleSelection:NO];
277 | [panel setCanChooseDirectories:YES];
278 | [panel setResolvesAliases:NO];
279 | [panel setCanChooseFiles:NO];
280 |
281 | [panel beginWithCompletionHandler:^(NSInteger result) {
282 | if (result == NSFileHandlingPanelOKButton) {
283 | NSURL* theDoc = [[panel URLs] objectAtIndex:0];
284 | NSMutableString *content =[[NSMutableString alloc]initWithCapacity:0];
285 | [content appendString:[theDoc path]];
286 | [content appendString:@"/linkMap.txt"];
287 | [_result writeToFile:content atomically:YES encoding:NSUTF8StringEncoding error:nil];
288 | }
289 | }];
290 | }
291 |
292 | - (void)appendResultWithSymbol:(SymbolModel *)model {
293 | NSString *size = nil;
294 | NSString *methodsize = nil;
295 | if (model.size / 1024.0 / 1024.0 > 1) {
296 | size = [NSString stringWithFormat:@"%.2fM", model.size / 1024.0 / 1024.0];
297 | } else {
298 | size = [NSString stringWithFormat:@"%.2fK", model.size / 1024.0];
299 | }
300 | [_result appendFormat:@"%@\t%@\r\n",size, [[model.file componentsSeparatedByString:@"/"] lastObject]];
301 |
302 |
303 | model.methodArray = [self sortMethodModel:model.methodArray];
304 |
305 |
306 | for (MethodModel *mothed in model.methodArray) {
307 |
308 | if (mothed.methodSize / 1024.0 / 1024.0 > 1) {
309 | methodsize = [NSString stringWithFormat:@"%.2fM",mothed.methodSize/ 1024.0 / 1024.0];
310 | } else {
311 | methodsize = [NSString stringWithFormat:@"%.2fK", mothed.methodSize / 1024.0];
312 | }
313 |
314 | [_result appendFormat:@" %@\t%@\r\n",methodsize , mothed.methodName];
315 | }
316 | }
317 |
318 | - (NSMutableArray *)sortMethodModel:(NSArray *) methodModels {
319 | NSArray *sortedSymbols = [methodModels sortedArrayUsingComparator:^NSComparisonResult(MethodModel * _Nonnull obj1, MethodModel * _Nonnull obj2) {
320 | if(obj1.methodSize > obj2.methodSize) {
321 | return NSOrderedAscending;
322 | } else if (obj1.methodSize < obj2.methodSize) {
323 | return NSOrderedDescending;
324 | } else {
325 | return NSOrderedSame;
326 | }
327 | }];
328 | NSMutableArray *array =[NSMutableArray arrayWithArray:sortedSymbols];
329 | return array;
330 | // return sortedSymbols;
331 | }
332 |
333 |
334 |
335 | - (BOOL)checkContent:(NSString *)content {
336 | NSRange objsFileTagRange = [content rangeOfString:@"# Object files:"];
337 | if (objsFileTagRange.length == 0) {
338 | return NO;
339 | }
340 | NSString *subObjsFileSymbolStr = [content substringFromIndex:objsFileTagRange.location + objsFileTagRange.length];
341 | NSRange symbolsRange = [subObjsFileSymbolStr rangeOfString:@"# Symbols:"];
342 | if ([content rangeOfString:@"# Path:"].length <= 0||objsFileTagRange.location == NSNotFound||symbolsRange.location == NSNotFound) {
343 | return NO;
344 | }
345 | return YES;
346 | }
347 |
348 | - (void)showAlertWithText:(NSString *)text {
349 | NSAlert *alert = [[NSAlert alloc]init];
350 | alert.messageText = text;
351 | [alert addButtonWithTitle:@"确定"];
352 | [alert beginSheetModalForWindow:[NSApplication sharedApplication].windows[0] completionHandler:^(NSModalResponse returnCode) {
353 | }];
354 | }
355 |
356 | @end
357 |
--------------------------------------------------------------------------------
/gui/LinkMap/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
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 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
707 |
708 |
709 |
710 |
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 |
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 |
795 |
796 |
797 |
798 |
799 |
800 |
801 |
802 |
--------------------------------------------------------------------------------