开发中一直在使用 cocoapods , git, class-dump, cycript 等强大的命令行工具. 
今天就来自己尝试做一个简单了解一下这些工具的工作原理.
开始前先讲个故事:
UI :
iOS 的 icon 都需要那些尺寸 ◔ ‸◔??我 :给我一张 1024 的就好 (-_-#). (思路被打断正不爽).
于是后来每次我都只能拿到一张 1024 * 1024 的 icon, 不得不每次都自己用 预览 来导出各种尺寸的 icon. 
毕竟自作自受  ("▔□▔)/ , 那今天就写个生成各种尺寸 icon 的工具吧.
0x00 如何开始
- 
    
首先使用 Xcode 创建一个
Command Line工程. 没错, 就是那种只有一个.m文件,run了之后只会printf和NSLog的那种工程, 我的项目名称就叫appicon, 后面会提到. - 导入头文件 
#import <Cocoa/Cocoa.h>, 因为是基于 macOS 的, 所以导入这个. 不然NSFileManager都用不了, 大概… - 先上代码, 下面再解释.
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString *input = nil;
        NSString *output = nil;
        
        int ch;
        // 读取参数
        while ((ch = getopt(argc, argv, "i:o:")) != -1) {
            switch (ch) {
                case 'o':
                    output = [NSString stringWithUTF8String:optarg];
                    break;
                case 'i':
                    input = [NSString stringWithUTF8String:optarg];
                    break;
            }
        }
        
        BOOL isDir = NO;
        // 判断文件路径, 以及输出目标路径
        if (input && output && [[NSFileManager defaultManager] fileExistsAtPath:output isDirectory:&isDir]) {
            NSImage *image = [[NSImage alloc] initWithContentsOfFile:input];
            if (image == nil) {
                printf("File Load Failed At %s", input.UTF8String);
                exit(0);
            }
            if (!isDir) {
                output = [output stringByAppendingPathComponent:@"icons"];
            }
            writeImage(image, output, 180);
            writeImage(image, output, 120);
            writeImage(image, output, 80);
            writeImage(image, output, 60);
            writeImage(image, output, 40);
        } else {
            printf("Path Error!\n");
        }
    }
    return 0;
}
也没多少代码, 看起来也没多难, 应该都能看懂, 除了这段:
1
2
3
4
5
6
7
8
9
10
11
int ch;
while ((ch = getopt(argc, argv, "i:o:")) != -1) {
switch (ch) {
    case 'o':
        output = [NSString stringWithUTF8String:optarg];
        break;
    case 'i':
        input = [NSString stringWithUTF8String:optarg];
        break;
    }
}
首先要注意一个函数 getopt, 这个就是获取命令行输入参数的关键.
1
2
// 函数返回 -1 表示参数解析完毕
int getopt(int argc, char * const argv[], const char *optstring);
看函数声明有没有熟悉的东西? 
我们的main 函数 int main(int argc, const char * argv[]) 里面有一模一样的 2 个参数.
argc:
main()函数传递过来的参数的个数 argv:main()函数传递过来的参数的字符串指针数组 optstring:选项字符串,告知getopt()可以处理哪个选项以及哪个选项需要参数
argc 和 argv 直接放进去就可以了, optstring 此处传入的是 "i:o:".
char* optstring = "ab:c::";
| 字符               | 意义                      | 格式                                |
| —————— | ————————- | ———————————– |
| 单个字符 a         | 表示选项a没有参数         | 格式:-a 即可,不加参数             |
| 单字符加冒号 b:    | 表示选项b有且必须加参数   | 格式:-b 100 或 -b100 ,但 -b=100 错 |
| 单字符加2冒号 c: : | 表示选项c可以有,也可以无 | 格式:-c200,其它格式错误           |
因此此处的 "i:o:", 表示有 2 个参数 -i 和 -o, 分别代表 input 和 output, 可以看成是参数名.
使用一个while 循环来获取所有的参数, 此处的 optarg 就是获取后的参数值.
以下写过 iOS 的稍微转换一下就可以了.都是常见的功能. 压缩图片尺寸:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 压缩图片到指定尺寸
 @param image 图片对象
 @param width 目标尺寸
 @return 压缩结果
 */
NSData * compressImage(NSImage *image, float width) {
    NSData *data = image.TIFFRepresentation;
    if (data == nil) {
        return nil;
    }
    CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)data, NULL);
    CGImageRef cgimage = CGImageSourceCreateImageAtIndex(source, 0, NULL);
    
    NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithCGImage:cgimage];
    rep.size = CGSizeMake(width, width);
    NSData *pngData = [rep representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
    return pngData;
}
把 image 写入指定的位置.
1
2
3
4
5
6
7
8
9
10
11
/**
 处理图片并把结果写入磁盘
 @param image 被处理的图片对象
 @param path  写入路径
 @param size  目标图片尺寸
 */
void writeImage(NSImage *image, NSString *path, float size) {
    path = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"%.0f.png", size]];
    [compressImage(image, size) writeToFile:path atomically:NO];
}
0x01 如何使用
- 项目编译之后会生成一个 
appicon,Product文件夹下的Mach-O文件. - 复制 
appicon到usr/local/bin, 放在此目录下, 在任何文件夹下都能够找到命令, 不然还要指定路径. - 使用命令:
 
1
$ appicon -i [输入文件路径] -o [输出目录路径]
例如:
1
$ appicon -i ~/Desktop/1024.png -o ~/Desktop
或者
1
2
$ cd ~/Desktop/
$ appicon -i ./1024.png -o ./
而上文中 input 和 output 分别就对应 ~/Desktop/1024.png 和 ~/Desktop.