冷雨之家

Dying is the day worth living for

将 iPhone 备份文件迁移到外置磁盘

| Comments

随着iPhone的不断升级迭代,手机闪存的容量也是越来越大。最新的 iPhone13 的起步闪存更是达到了128GB。这样的话,如果使用电脑的内置硬盘来存储iPhone的备份,那么将会浪费巨大的硬盘空间。比如,我的iPhone 13 pro,备份文件占硬盘70多个G。我平时主要是使用mac电脑,内置硬盘空间不可扩充,内置硬盘空间简直是寸土寸金。

本文主要介绍几种将iPhone的备份文件位置从内置硬盘迁移到外置硬盘(移动硬盘)上的方法。

方法一: 使用第三方备份软件

有一些第三方的备份软件,提供了自定义备份文件存放路径的选项, 比如 iMazing

可以直接在 [偏好设置] → [备份] 中直接修改默认的备份位置。

优点

  • 操作简单,方便
  • iMazing的备份速度似乎比系统的快
  • 三方软件提供了一些查看备份内容的功能,不过这个基本上用处不大,因为这本来就是备份文件,主要是用来防止原始文件丢失的,不用用来查看,操作文件的。

缺点:

  • iMazing是一款收费软件,好在单机版license 不怎么贵,大概是¥100左右。

方法二:使用软链接将系统默认的备份位置链接到移动硬盘下的备份目录下

Mac OS存放iPhone备份文件的位置是:

1
~/Library/Application Support/MobileSync/Backup

Backup下每个文件夹代表一台设备。文件夹的命名不具有可读性,类似:

1
00008020-000D389C366A002E 00008110-001421520A2A801E

Setps:

  1. 将 Backup 文件夹下所有的文件移到外置硬盘上,假设外置硬盘的路径为:/Volumes/EcternalDisk/ios_backups

  2. 删除~/Library/Application Support/MobileSync/Backup 文件夹

  3. 使用软链接关联 ~/Library/Application Support/MobileSync/Backup/Volumes/EcternalDisk/ios_backups, 具体的命令为:

    ln -s /Volumes/EcternalDisk/ios_backups ~/Library/Application\ Support/MobileSync/Backup
    

这样,以后每次使用 iTunes或者Finder(maxOS Big Sur 11.6 中,iTunes中的备份功能被拆分到了Finder中)备份iPhone的时候,备份文件会自动存储到外置硬盘上。 这种方法的本质就是用软链接欺骗了系统。

优点

  • 使用 macOS 原生的备份工具,不需要借助第三方软件。

缺点

  • 备份速度超级慢,手机闪存容量稍微大一些(>16GB),就慢到无法使用。

实际体验这个方法之后,我终于理解了为啥Apple不在 iTunes 中提供一个改变备份文件存储路径的选项了。我实际用128GB和256GB的手机测试了下,跑了一晚上,7-8个小时,结果备份没完成…, 分析了一个可能有一下原因:

  • 内置的硬盘是固态硬盘,外置硬盘一般都是机械硬盘,在数据传输速度上,机械硬盘远远低于固态硬盘
  • 备份文件琐碎,文件体积小,数量多,总体的体积却很大,这更加加重了外置硬盘的传输负担
  • USB接口速率限制,我有一块老硬盘是USB2.0的接口,简直是雪上加霜。

方法三(推荐):打成zip文件放在外置硬盘上

这种方法就比较简单了,既然备份文件就放在这里。

1
~/Library/Application Support/MobileSync/Backup

那么,我只需要在备份完成之后,把每个设备文件夹打个zip包,扔到外置硬盘上就行,等需要用到备份文件的时候,再迁移回来即可。

优点

  • macOS 系统原生备份

缺点

  • 需要在备份的时候临时占用电脑内置硬盘的一部分空间

Link

[将你的 iOS 设备备份到外置磁盘 | 一日一技 · Mac]

https://www.imore.com/how-move-your-iphone-or-ipad-backups-external-hard-drive

OpenWRT下安装和配置shadowsocks

| Comments

本文主要记录在openWRT下安装和配置shadowsocks的简要过程,便于日后查找和备忘。成功安装后可以实现透明代理,分流和防DNS污染。

Environment

  • 路由器型号:YouHua WR1200JS
  • 固件版本:OpenWrt 19.07.4 r11208-ce6496d796 / LuCI openwrt-19.07 branch git-21.054.03371-3b137b5

拓扑图+工作原理

topology map

  1. dnsmasq是openwrt自带的一个软件,提供dns缓存,dhcp等功能。dnsmasq会将dns查询数据包转发给chinadns。

  2. chinadns的上游DNS服务器有两个,一个是国内DNS,一个是可信DNS(国外DNS)。

    • chinadns会同时向上游的DNS发送请求
    • 如果可信DNS先返回, 则直接采用可信DNS的结果
    • 如果国内DNS先返回, 分两种情况: 如果返回的结果是国内IP,则采用;否则丢弃并等待采用可信DNS的结果

3.dns-forwarder 支持DNS TCP查询, 如果ISP的UDP不稳定, 丢包严重,可以使用dns-forwarder来代替ss-tunnel来进行DNS查询.

4.shadowsocks 用于转发数据包, 科学上网. 关于shadowsocks的科普文章可查看这里: https://www.css3er.com/p/107.html

相关的ipk软件包下载地址

ipk软件包集合, 不同的CPU架构需要使用不同的软件包, CPU架构是mipsel_24kc的话, 可以集中从这里下载.
链接: https://pan.baidu.com/s/14QDoTLqw-SEBZvQVQeVgvA 提取码: ugsc
其它的CPU架构, 可以去GitHub主页 -> Releases下载别人已经编译好的软件包, 如果没有, 只能自己下载openWRT的SDK, 自己进行编译.

  • shadowsocks-libev_3.3.5-1_mipsel_24kc.ipk
  • shadowsocks-libev-server_3.3.5-1_mipsel_24kc.ipk
  • ChinaDNS_1.3.3-1_mipsel_24kc.ipk
  • dns-forwarder_1.2.1-2_mipsel_24kc.ipk
  • luci-compat
  • luci-app-shadowsocks-without-ipset_1.9.1-1_all.ipk
  • luci-app-chinadns_1.6.2-1_all.ipk
  • luci-app-dns-forwarder_1.6.2-1_all.ipk

链接: https://pan.baidu.com/s/14QDoTLqw-SEBZvQVQeVgvA 提取码: ugsc

openwrt-shadowsocks

GitHub: https://github.com/shadowsocks/openwrt-shadowsocks
luci-app-shadowsocks: https://github.com/shadowsocks/luci-app-shadowsocks

  • shadowsocks-libev

     客户端/
     └── usr/
         └── bin/
             ├── ss-local       // 提供 SOCKS 正向代理, 在透明代理工作模式下用不到这个.
             ├── ss-redir       // 提供透明代理, 从 v2.2.0 开始支持 UDP
             └── ss-tunnel      // 提供端口转发, 可用于 DNS 查询
    
  • shadowsocks-libev-server

    服务端/
    └── usr/
        └── bin/
            └── ss-server      // 服务端可执行文件
    

ChinaDNS

GitHub: https://github.com/aa65535/openwrt-chinadns
原版ChinaDNS地址, 被请喝茶后已不再维护:https://github.com/shadowsocks/ChinaDNS
luci-app-chinadns: https://github.com/aa65535/openwrt-dist-luci

更新 /etc/chinadns_chnroute.txt

1
 wget -O- 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest' | awk -F\| '/CN\|ipv4/ { printf("%s/%d\n", $4, 32-log($5)/log(2)) }' > /etc/chinadns_chnroute.txt

dns-forwarder

GitHub: https://github.com/aa65535/openwrt-dns-forwarder
luci-app-dns-forwarder: https://github.com/aa65535/openwrt-dist-luci

dnsmasq

openWRT自带, 无需自行下载安装.
GitHub: https://github.com/aa65535/openwrt-dnsmasq

Install

去软件项目的GitHub主页 -> Releases下面下载编译好的ipk, 如果没有符合的自己CPU架构的包, 则需要自己下载openWRT的SDK进行编译, 具体的教程各个主页上有.
查看CPU架构的命令 opkg print-architecture:

1
2
3
4
5
root@OpenWrt:~# opkg print-architecture
arch all 1
arch noarch 1
arch mipsel_24kc 10
root@OpenWrt:~#

下载完成有两种方式安装
方式一(建议): 通过web使用luci安装: 路径: 系统 -> Software -> Upload Package… -> Install

方式二: 直接在线通过opkg命令来安装(注意使用方式需要提前更新好软件源, opkg update):

1
opkg install luci-compat

Config

方式一, 使用luci来配置

登录luci.

  1. 配置ss-server
    服务 -> 影梭 -> 服务器管理, 添加自己的shadowsocks server
  2. 配置dnsmasq
    • 网络 -> DHCP/DNS -> 常规设置 -> 本地服务器, 设置为 127.0.0.1#5353
    • 网络 -> DHCP/DNS -> HOSTS和解析文件, 勾选: 忽略解析文件
  3. 配置ChinaDNS
    服务 -> ChinaDNS
    监听端口: 5353
    上游服务器修改为: 114.114.114.114,127.0.0.1#5300
    这样国内DNS: 114.114.114.114, 可信DNS: 127.0.0.1#5353, 勾选 启用, 保存设置
  4. 配置dns-forwarder
    服务 -> DNS转发
    监听端口: 5300 监听地址: 0.0.0.0
    上游 DNS: 8.8.8.8 勾选, 启用 保存
  5. 配置shadowsocks 透明代理 + 访问控制
    服务 -> 影梭 -> 常规设置 -> 透明代理
    主服务器, 选择setp1中配置的ss-server, 保存.
    服务-> 影梭 -> 常规设置 -> 访问控制-> 外网区域
    被忽略IP列表, 选择 ChinaDNS路由表, 保存设置. 注意这里的优先级: (走代理IP列表 = 强制走代理IP) > (额外被忽略IP = 被忽略IP列表)

  6. 保存并应用 所有配置, reboot openWRT

方式二, 直接编辑/etc/config目录下的文件

课外阅读: UCI System UCI system

The abbreviation UCI stands for Unified Configuration Interface and is intended to centralize the configuration of OpenWrt.

/etc/config/shadowsocks

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
root@OpenWrt:~# cat /etc/config/shadowsocks

config general
  option startup_delay '0'

config transparent_proxy
  option udp_relay_server 'nil'
  option local_port '1234'
  option mtu '1492'
  list main_server 'cfg054a8f'

config socks5_proxy
  option local_port '1080'
  option mtu '1492'
  list server 'nil'

config port_forward
  option local_port '5300'
  option mtu '1492'
  option destination '8.8.8.8:53'
  list server 'nil'

config servers
  option fast_open '0'
  option no_delay '0'
  option timeout '60'
  option server '服务器地址,注意luci下这里只能是ip'
  option server_port '端口'
  option password '密码'
  option encrypt_method '加密方式'
  option alias 'ss服务别名'

config access_control
  option self_proxy '1'
  option lan_target 'SS_SPEC_WAN_AC'
  option wan_bp_list '/etc/chinadns_chnroute.txt'

/etc/config/dhcp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
root@OpenWrt:~# cat /etc/config/dhcp

config dnsmasq
  option domainneeded '1'
  option localise_queries '1'
  option rebind_protection '1'
  option rebind_localhost '1'
  option domain 'lan'
  option expandhosts '1'
  option authoritative '1'
  option readethers '1'
  option leasefile '/tmp/dhcp.leases'
  option localservice '1'
  option local '127.0.0.1#5353'
  option noresolv '1'
...

/etc/config/chinadns

1
2
3
4
5
6
7
8
9
root@OpenWrt:~# cat /etc/config/chinadns

config chinadns
  option chnroute '/etc/chinadns_chnroute.txt'
  option addr '0.0.0.0'
  option port '5353'
  option bidirectional '1'
  option server '114.114.114.114,127.0.0.1#5300'
  option enable '1'

/etc/config/dns-forwarder

1
2
3
4
5
6
7
root@OpenWrt:~# cat /etc/config/dns-forwarder

config dns-forwarder
  option listen_addr '0.0.0.0'
  option listen_port '5300'
  option enable '1'
  option dns_servers '8.8.8.8'

验证配置是否生效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
root@OpenWrt:~# netstat -lpn | grep ss
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:1234            0.0.0.0:*               LISTEN      13469/ss-redir
root@OpenWrt:~# netstat -lpn | grep 5353
udp        0      0 0.0.0.0:5353            0.0.0.0:*                           1438/chinadns
root@OpenWrt:~# netstat -lpn | grep 5300
udp        0      0 0.0.0.0:5300            0.0.0.0:*                           12993/dns-forwarder
root@OpenWrt:~# netstat -lpn | grep 53
tcp        0      0 127.0.0.1:53            0.0.0.0:*               LISTEN      2254/dnsmasq
...

root@OpenWrt:~# nslookup google.com 127.0.0.1#5353
Server:       127.0.0.1
Address:  127.0.0.1#5353

Name:      google.com
Address 1: 142.250.72.238
Address 2: 2607:f8b0:4007:80d::200e
root@OpenWrt:~#

Issues

  • luci-app-shadowsocks 不支持domain的方式配置ss-server, 需要使用IP地址

Link

https://www.youtube.com/watch?v=2SPQYsMmltE&t=317s - 十年老程 openwrt shadowsocks安装配置对应的视频教程 http://snlcw.com/305.html - 上述教程对应的blog地址. https://www.youtube.com/channel/UCgo7XWK6MQBgKt0gBI6x3CA/videos - 十年老程的Youtube专栏,里面有各种科学上网的视频教程. https://openwrt.org/docs/guide-user/base-system/dhcp_configuration

Gitignore 文件屏蔽规则

| Comments

文件名: .gitignore

位置:

global 当前用户的主目录下 ~ 一般是自己使用不和别人share 该设置对所有的本地仓库都起作用

local 当前仓库的主目录下 一般需要加入到git库的版本控制中,需要和别人share

git config 中的 core.excludesfile 可以指定 .gitignore 文件

格式规范如下:

  • 所有空行和#开头的行都会被忽略
  • 文件或者目录前加 / 表示仓库根目录下的对应文件,子目录下的同名文件不忽略
  • 文件或者目录后加 / 表示要忽略的是目录,不加 / 表示文件和目录都忽略
  • 所有模式取反可以在最前面加 !
  • 可以使用标准的 glob 模式匹配

glob 模式是一种简化了的正则表达式,使用于shell - * 匹配零个或者多个任意字符 - ?匹配任意一个字符 - [abc] 匹配任意一个方括号中的字符 - [0-9] 短线表示范围,表示匹配任意一个0到9的数字 - {string1,string2} 大括号代表可选的字符串

匹配时,下面的条目可以覆盖上

1
2
readme.md       # 屏蔽仓库中所有名为 readme.md 的文件
!/readme.md     # 在上一条屏蔽规则的条件下,不屏蔽仓库根目录下的 readme.md 文件

Link

https://www.jianshu.com/p/13612fb4b224 https://www.cnblogs.com/qwertWZ/archive/2013/03/26/2982231.html

C 语言函数指针小测试

| Comments

首先看一道趣味题,来源:https://www.v2ex.com/t/492705

1
2
3
4
5
6
7
#include <stdio.h>

int main() {
[]
printf("%p\n", **********************p);
return 0;
}

在 [] 标记内加入一条语句,使得:

  1. 程序编译通过,无错误(警告随意);链接通过,无错误(警告随意)
  2. 程序能够运行,无崩溃和运行时错误
  3. 源代码为.c 文件
  4. 如果用 vs,则使用 Release 配置;用 gcc 也行
  5. 加入的语句不能忽略分号
  6. printf 一句必须能够正常输出内容

答案如下表

int(*p)(); 10个字符 兼容 gcc、g++、vs
int p(){} 9个字符 兼容 gcc,不兼容 g++、vs
p();}p(){ 9个字符 兼容 gcc、g++、vs

本文主要针对第一种答案来说明一下为什么函数指针可以被无线次解引用.

& *

说到指针,首先就一定要介绍两个操作符.

& 地址操作符(address operators) 单目前缀操作符,操作数为跟在后面的表达式. eg:&a,表示取操作数a的地址,也可以理解为取对象a的地址,或者取变量a的地址等等.

1
2
3
4
5
6
// 在64处理器的系统模式下,地址的长度为8个字节
int a;
printf("sizeof(&a)=%d\n",sizeof(&a));

**********result**********
sizeof(&a)=8

* 间接操作符 (indirection operators) 单目前缀操作数,同样也是跟在后面的表达式为操作数,可以使用间接操作符通过指针对象间接地访问它所指向对象的值. eg: *a,表示取指针对象a中存的地址所指向的值. (这里的表达非常绕,下面举个例子,请自行脑补)

a的举例: 超市的储物柜,每个格子都有一个唯一对应的条形码,我们认为这就是每个格子的地址,格子本身就是实实在在的三维空间,是可以放物品的.现在有两个格子a格和b格,我们把背包放进了b格,然后把印有b格条码的纸条放进了a格, 那么,a格就相当于一个指针变量, b格就相当于一个普通的变量. a的意思就是 分两步,第一步找到a格,取出a格中存放的条码,第二步,按照条码找到b格,取出b格中的背包. 最终简化一下, *a就是背包.

1
2
3
4
5
6
7
int b = 666;
int *a;
a = &b;
printf("*a=%d sizeof(*a)=%d\n",*a,sizeof(*a));

**********result**********
*a=666 sizeof(*a)=4

对于间接操作符,需要特别注意的有两点:

第一,间接操作符(*)与用来声明之神类别对象的”*“不属于同种功能,虽然它们两者确实是同一个字符,unicode码点都是 \u002a

第二,间接操作符只作用于指针类型的对象,也就是说简介操作符的的操作数必须是一个指针类型的对象.

“取地址”与”解引用”

通俗的讲,对一个变量的 & 操作称为”取地址”

对一个指针变量的 * 操作称作”解引用”.

换一种角度去理解解引用,”*“的作用是引用指针指向的变量值,引用其实就是引用该变量的地址,“解”就是把该地址对应的东西解开,解出来,就像打开一个包裹一样,那就是该变量的值了,所以称为“解引用”。也就是说,解引用是返回内存地址中保存的值。这个值可以是另一个地址。

多级指针.

如果一个指针变量存的是一个普通变量的地址,比如 int a,只能存放一个整型变量的地址,那么这个指针叫做一级指针;如果一个指针变量存放的是另一个指针变量的地址,比如 int **, 里面存放的是一个(int a)类型的指针变量的地址,那么这个指针变量叫二级指针,依次往下推,可以有三级指针,四级指针….直到多级指针.

函数指针

简单理解就是指向函数地址的指针。比如我们声明的一个C函数

1
2
3
void func(void);
//函数调用
func()

那么对于函数调用表达式 func() 而言, func后缀表达式就已经表示了一个指向返回类型为void,且参数列表为空的函数的指针:

void (*) (void)

函数指针的通用表达形式为:

返回类型 (* cv限定符 )(形参列表)

其中,cv限定符为可选项。

直接上code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void (*p)();
p=&func;

printf("sizeof(func)= %d\n",sizeof(func));
printf("sizeof(p)= %d\n",sizeof(p));

printf("func\t %p = %p\n",&func,func);
printf("p *p\t %p = %p = %p\n",&p,p,*p);

printf("p多次解引用 %p\n", **********************p);

void (*pNull)();
printf("pNull *p\t %p = %p = %p\n",&pNull,pNull,*pNull);
printf("pNull多次解引用 %p\n", **********************p);

**********result**********
sizeof(func)= 1
sizeof(p)= 8
func    0x100000df0 = 0x100000df0
p *p  0x7ffeefbff568 = 0x100000df0 = 0x100000df0
p多次解引用 0x100000df0
pNull *p  0x7ffeefbff560 = 0x0 = 0x0
pNull多次解引用 0x0

可以看出:

  1. 函数指针变量在64位系统下的大小是8,函数指针常量的大小是1。

  2. 函数指针常量中存储的内容是自身的地址(这是为什么函数指针可以被无限次解引用的关键,因为解到最后一层以后,继续解就陷入了”自己找自己“的循环)。

  3. 未赋值的函数指针变量默认指向的是地址是0x0,对存储在该地址的指针对象解引用的结果依旧是0x0,所以可以认为0x0地址的内存单元中存的是地址0x0(此处上可能有误,尚需以后讨论)。

Chrome中使用英文关键词搜索中文结果

| Comments

在国内,访问Google需要用一点小技巧.有时候,我们使用英文的关键词,但是搜索结果想要查看中文的.由于的这点小技巧的原因,导致Google不能够正确的识别国家和地区的设置.

知乎中有一篇帖子介绍了几个修改的方法: 如何修改Chrome里Google搜索的国家和地区设置? 这里我采取另外的一种方法来实现.

  1. chrome地址栏中输入 chrome://settings/searchEngines 打开chrome中的管理搜索引擎

  2. 复制默认搜索引擎的查询网址: {google:baseURL}search?q=%s&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:iOSSearchLanguage}{google:searchClient}{google:sourceId}{google:contextualSearchVersion}ie={inputEncoding}

  3. 在上面的String后面追加 &lr=lang_zh-CN, 然后添加一个搜索引擎, 名字: Google 中文 关键词: cn 查询网址: {google:baseURL}search?q=%s&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:iOSSearchLanguage}{google:searchClient}{google:sourceId}{google:contextualSearchVersion}ie={inputEncoding}&lr=lang_zh-CN

增加搜索引擎

4.保存后,在地址栏中输入 cn+空格 ,然后再输入搜索关键字,OK,展示的搜索结果已经变成中文结果了.

使用介绍

Xcode 使用技巧

| Comments

Alcatraz

Alcatraz是一款开源的用于Xcode7的插件管理工具.Xcode8以及以后版本需要配合下面的工具来使用.

update_xcode_plugins

update_xcode_plugins可以为Xcode中安装的插件添加UUID, 同时还能够 为Xcode8以及以上的版本解除签名,这样就可以随心所欲的使用各种Xcode插件了.

Reset Xcode’s “Load Bundles” warning

1
defaults delete com.apple.dt.Xcode DVTPlugInManagerNonApplePlugIns-Xcode-10.0

执行该命令,重启Xcode,可以使Xcode弹出”Load Bundles”的提示,可以重新load所有的插件.注意command末尾处的Xcode的版本号.

添加删除行 快捷键 Opt+D

  1. 修改配置文件(plist)权限

    sudo chmod 666 /Applications/Xcode.app/Contents/Frameworks/IDEKit.framework/Resources/IDETextKeyBindingSet.plist
    sudo chmod 777 /Applications/Xcode.app/Contents/Frameworks/IDEKit.framework/Resources/
    
  2. 打开plist文件进行修改

    open /Applications/Xcode.app/Contents/Frameworks/IDEKit.framework/Resources/IDETextKeyBindingSet.plist
    

    找到root下的Deletions,在Deletions下添加一个Key: Delete Current Line 值为deleteToBeginningOfLine:, moveToEndOfLine:, deleteToBeginningOfLine:, deleteBackward:, moveDown:, moveToBeginningOfLine:

  3. 重启Xcode,设置快捷键

Preference -> Key Bindings ,找到 Delete Current Line 选项,设置快捷键为 Opt+D

Xcode Release Build 版本号自动增加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if [ $CONFIGURATION == Release ]; then
echo "Bumping build number..."
plist=${PROJECT_DIR}/${INFOPLIST_FILE}

buildnum=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${plist}")
if [[ "${buildnum}" == "" ]]; then
echo "No build number in $plist"
exit 2
fi

buildnum=$(expr $buildnum + 1)
/usr/libexec/Plistbuddy -c "Set CFBundleVersion $buildnum" "${plist}"
echo "Bumped build number to $buildnum"

else
echo $CONFIGURATION " build - Not bumping build number."

fi

使用方法 Xcode–>Projet–>Target–>Build Phases–>“+”–>New Run Script Phase. 顺序放在Target Dependencies之后即可,尽量靠前.

过程, 查找 Info.plist 文件的位置,使用工具 /usr/libexec/PlistBuddy 读取 CFBundleVersion 的值,+1后再写会 Info.plist 文件.

Lipo 使用简介

| Comments

最近在开发iOS SDK, 需要使用到lipo命令, 在此简单做一下记录:

lipo的使用手册

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
➜  ~  man lipo

LIPO(1)                                                                     LIPO(1)



NAME
       lipo - create or operate on universal files

SYNOPSIS
       lipo   [-info]   [-detailed_info]   [-arch   arch_type  input_file]  ...   [
       input_file]  ...   [-arch_blank  arch_type]  [-create]   [-thin   arch_type]
       [-replace  arch_type  filename]  ...   [-remove  arch_type]  ...lio   [-extract
       arch_type] ...  [-extract_family  arch_type]  ...   [-verify_arch  arch_type
       ...]  [-output output_file] [-segalign arch_type value] ...

DESCRIPTION
       The  lipo  command creates or operates on ``universal'' (multi-architecture)
       files.  It only ever produces one output file, and never  alters  the  input
       file.  The operations that lipo performs are: listing the architecture types
       in a universal file; creating a single universal file from one or more input
       files;  thinning  out  a single universal file to one specified architecture
       type; and extracting, replacing, and/or removing  architectures  types  from
       the input file to create a single new universal output file.

       Only  one option can be specified, with the exception of -arch, -arch_blank,
       -output, and -segalign, which are used in combination  with  other  options.
       The input_file argument is required, and only the -create option allows more
       than one input_file to be specified.  The -output flag must be used,  except
       with the -info and -detailed_info flags.

       The  arch_type  arguments  may  be  any  of the supported architecture names
       listed in the man page arch(3).

简单说,lipo就是用来操作universal file的命令,也可以说成是 fat file(因为这种文件确实比较大).在iOS中其实就是用来操作静态链接库(以.a为后缀的文件 )的命令.

fat file一般是指包含两种及以上CPU架构的静态链接库

lipo命令的功能:

  • 列举 .a文件 支持的architecture types.(架构类型,eg: armv7, armv7s, i386, x86_64, arm64)
  • 合并多个 .a文件为一个.a文件
  • 给一个 .a文件 瘦身.(剔除某一项CPU的架构类型)
  • 提取, 替换或者移除 .a文件 文件中的某种架构

使用lipo命令的注意事项

  • 除了 -arch, -arch_black, -output, -segalign选项外,其余的选项只能指定一个. 这几个选选项需要和其它的选项混合使用
  • 除了 -create 选项外, 其余的选项只能指定一个input file作为参数
  • 除了 -info 和 - detailed_info 选项外, 其余的选项必须指定 -output, 可以简写为 -o

lipo命令的使用eg,以WechatSDK 1.8.2作为举例对象

1.查看.a文件支持的CPU架构

lipo -info input_file

lipo -detailed_info input_file

➜  workspace lipo -info libWeChatSDK.a 
Architectures in the .a文件: libWeChatSDK.a are: i386 armv7 armv7s x86_64 arm64 

➜  workspace lipo -detailed_info libWeChatSDK.a
Fat header in: libWeChatSDK.a
fat_magic 0xcafebabe
nfat_arch 5
architecture i386
    cputype CPU_TYPE_I386
    cpusubtype CPU_SUBTYPE_I386_ALL
    offset 108
    size 3247680
    align 2^2 (4)
architecture armv7
    cputype CPU_TYPE_ARM
    cpusubtype CPU_SUBTYPE_ARM_V7
    offset 3247788
    size 3256064
    align 2^2 (4)
architecture armv7s
    cputype CPU_TYPE_ARM
    cpusubtype CPU_SUBTYPE_ARM_V7S
    offset 6503852
    size 3255048
    align 2^2 (4)
architecture x86_64
    cputype CPU_TYPE_X86_64
    cpusubtype CPU_SUBTYPE_X86_64_ALL
    offset 9758904
    size 3363856
    align 2^3 (8)
architecture arm64
    cputype CPU_TYPE_ARM64
    cpusubtype CPU_SUBTYPE_ARM64_ALL
    offset 13122760
    size 3704912
    align 2^3 (8)
➜  workspace 

2..a文件 CPU架构拆分

lipo 静态库源文件路径 -thin CPU架构名称 -output 拆分后文件存放路径

1
2
3
➜  workspace lipo -info libWeChatSDK-armv7
input file libWeChatSDK-armv7 is not a fat file
Non-fat file: libWeChatSDK-armv7 is architecture: armv7

3.合并两个 .a文件s

lipo -create 静态库存放路径1  静态库存放路径2 …  -output 整合后存放的路径

1
2
3
4
5
6
7
➜  workspace lipo -create libWeChatSDK-armv7 libWeChatSDK-i386 -o libWeChatSDK-i386+armv7
➜  workspace lipo -info libWeChatSDK-i386+armv7
Architectures in the fat file: libWeChatSDK-i386+armv7 are: armv7 i386
➜  workspace

➜  workspace lipo -create libWeChatSDK.a TYRZSD.a -o combine-library.a
fatal error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/lipo: libWeChatSDK.a and TYRZSD.a have the same architectures (i386) and can't be in the same fat output file

这里需要注意一下,如果要合并的两个.a 文件含有相同的架构的话,会报如上的错误. 这种情况下怎么合并,可参照下一个章节

4.用 lipo 删除某些平台

$ lipo input_file -remove i386 -output output_file

1
2
3
➜  workspace lipo libWeChatSDK.a -remove i386 -o libWeChatSDK-noi386
➜  workspace lipo -info libWeChatSDK-noi386
Architectures in the fat file: libWeChatSDK-noi386 are: armv7 armv7s x86_64 arm64

lipo命令合并.a文件, detail

1.分离出上述不同 .a 文件单独支持的架构版本,比如上述 libWeChatSDK.a 和 TYRZSD.a 支持 armv7 armv7s i386 x86_64 arm64,那么就需要分离出单独支持armv7 armv7s i386 x86_64 arm64的 .a 文件

1
2
➜  workspace lipo libWeChatSDK.a -thin armv7 -o libWeChatSDK-armv7
➜  workspace lipo TYRZSD.a -thin armv7 -o TYRZSD-armv7

2.分离出.a库的目标文件(.o)

 ar -x 静态链接库

因为 .a 库分离出来的目标文件 .o 会很多, 所以, 最好是在工作空间根据不同的CPU建立不同的文件夹

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
➜  workspace mkdir armv7
➜  workspace cd armv7
➜  armv7
➜  armv7 ar -x ../libWeChatSDK-armv7
➜  armv7 ls
AppCommunicate.o          MTAPlugin.o               OpenUDID.o
AppCommunicateData.o      MTAPluginIDFA.o           WXApi.o
GCDAsyncSocket.o          MTAPluginInstall.o        WXApiObject.o
MTA.o                     MTAPluginNotify.o         WXLogUtil.o
MTABaseFunction.o         MTAReachability.o         WapAuthHandler.o
MTAConfig.o               MTASendItem.o             WeChatApiUtil.o
MTADataConfigHolder.o     MTASocket.o               WeChatRegister.o
MTAEnv.o                  MTASpeedTestPlugin.o      WechatAuthSDK.o
MTAEvent.o                MTAStore.o                __.SYMDEF
MTAExtStoragePlugin.o     MTATempID.o               base64.o
MTAHelper.o               MTAWX.o
MTAMidPlugin.o            NSMutableArray+MTAQueue.o
➜  armv7 ar -x ../TYRZSD-armv7
➜  armv7 ls
AppCommunicate.o          MTAReachability.o         UAOpenInfo.o
AppCommunicateData.o      MTASendItem.o             UASession.o
GCDAsyncSocket.o          MTASocket.o               UATimer.o
MTA.o                     MTASpeedTestPlugin.o      UAUtil.o
MTABaseFunction.o         MTAStore.o                WXApi.o
MTAConfig.o               MTATempID.o               WXApiObject.o
MTADataConfigHolder.o     MTAWX.o                   WXLogUtil.o
MTAEnv.o                  NSMutableArray+MTAQueue.o WapAuthHandler.o
MTAEvent.o                OpenUDID.o                WeChatApiUtil.o
MTAExtStoragePlugin.o     TYRZSDK.o                 WeChatRegister.o
MTAHelper.o               TYRZSDK_vers.o            WechatAuthSDK.o
MTAMidPlugin.o            UAAuthViewController.o    __.SYMDEF
MTAPlugin.o               UACrypto.o                __.SYMDEF SORTED
MTAPluginIDFA.o           UAHTTP.o                  base64.o
MTAPluginInstall.o        UALogReport.o
MTAPluginNotify.o         UANetwork.o

3.对支持同个架构的.o文件,进行合并成静态库。

libtool -static -o output_file *.o

1
2
3
4
5
➜  armv7 libtool -static -o ../combine-library-armv7 *.o
➜  armv7 ls ..
TYRZSD-armv7          armv7                 libWeChatSDK-armv7    libWeChatSDK.a
TYRZSD.a              combine-library-armv7 libWeChatSDK-i386
➜  armv7

4.合成支持全部架构的通用静态库

如果之前的步骤不出错, 就会得到5个.a文件; combine-library-armv7 combine-library-armv7s combine-library-i386 combine-library-x86_64 combine-library-arm64

1
➜  workspace lipo -create combine-library-armv7 combine-library-armv7s combine-library-i386 combine-library-x86_64 combine-library-arm64 -o combine-library

​ combine-library 就是最终需要的 .a 库

参考链接

合并多个.a文件

秘钥格式 证书

| Comments

RSA秘钥存储一般有两种格式

  • DER
  • PEM

DER

DER: Distinguished Encoding Rules(可辨别编码规则),是ASN.1的一种。 ASN.1: Abstract Syntax Notation One(抽象语法标记),ASN.1是一种 ISO/ITU-T 标准,描述了一种对数据进行表示、编码、传输和解码的数据格式。它提供了一整套正规的格式用于描述对象的结构,而不管语言上如何执行及这些数据的具体指代,也不用去管到底是什么样的应用程序。 证书信息一般以二进制的DER格式存储在文件中以供RSA,SSL使用。

PEM ( Privacy Enhanced Mail )

DER一般是二进制文件形式存储,打印性较差,因此对DER内容进行base64编码,并补充说明key类型的头和尾就构成了PEM

1
2
3
4
5
6
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMYfnvWtC8Id5bPKae5yXSxQTt
+Zpul6AnnZWfI2TtIarvjHBFUtXRo96y7hoL4VWOPKGCsRqMFDkrbeUjRrx8iL91
4/srnyf6sh9c8Zk04xEOpK1ypvBz+Ks4uZObtjnnitf0NBGdjMKxveTq+VE7BWUI
yQjtQ8mbDOsiLLvh7wIDAQAB
-----END PUBLIC KEY-----

因此PEM,DER实质内容是相同的。

上面提到key以一定的结构存储的,不同的结构,补充的元信息也不同主要有两种组织形式PKCS#1,PKCS#8

PEM的简单介绍

PEM全称是Privacy Enhanced ,该标准定义了加密一个准备要发送邮件的标准 。它的基本流程是这样的:

  1. 信息转换为ASCII码或其它编码方式;  

  2. 使用对称算法加密转换了的邮件信息;  

  3. 使用BASE64对加密后的邮件信息进行编码;  

  4. 使用一些头定义对信息进行封装,这些头信息格式如下(不一定都需要,可选的 ):    

    Proc-Type,4:ENCRYPTED  
     DEK-Info: cipher-name, ivec    

    其中,第一个头信息标注了该文件是否进行了加密,该头信息可能的值包括ENCRY PTED(信息已经加密和签名)、MIC-ONLY(信息经过数字签名但没有加密)、MIC-CLEAR(信 息经过数字签名但是没有加密、也没有进行编码,可使用非PEM格式阅读)以及CLEAR(信 息没有签名和加密并且没有进行编码,该项好象是openssl自身的扩展,但是并没有真正 实现);;第二个头信息标注了加密的算法以及使用的ivec参量,ivec其实在这儿提供的 应该是一个随机产生的数据序列,与块加密算法中要使用到的初始化变量(IV)不一样 。  

  5. 在这些信息的前面加上如下形式头标注信息:  

    —–BEGIN PRIVACY-ENHANCED MESSAGE—–  

     在这些信息的后面加上如下形式尾标注信息:  

    —–END PRIVACY-ENHANCED MESSAGE—–  

上面是openssl的PEM文件的基本结构,需要注意的是,Openssl并没有实现PEM的全 部标准,它只是对openssl中需要使用的一些选项做了实现,详细的PEM格式,请参考RF C1421-1424。  

下面是一个PEM编码的经过加密的DSA私钥的例子:  

1
2
3
4
5
-----BEGIN DSA PRIVATE KEY-----     
Proc-Type: 4,ENCRYPTED     
DEK-Info: DES-EDE3-CBC,F80EEEBEEA7386C4     
BASE64 ENCODED DATA
-----END DSA PRIVATE KEY-----   

  有时候PEM编码的东西并没有经过加密,只是简单进行了BASE64编码,下面是一个没 有加密的证书请求的例子:

1
2
3
-----BEGIN CERTIFICATE REQUEST-----     
BASE64 ENCODED DATA
-----END CERTIFICATE REQUEST----- 

    可以看到,该文件没有了前面两个头信息。大家如果经常使用openssl的应用程序, 就对这些文件格式很熟悉了。

PKCS#1

PKCS#1结构仅为RSA设计

  • PEM形式

PublicKey

1
2
3
-----BEGIN RSA PUBLIC KEY-----
BASE64 ENCODED DATA
-----END RSA PUBLIC KEY-----

PrivateKey

1
2
3
-----BEGIN RSA PRIVATE KEY-----
BASE64 ENCODED DATA
-----END RSA PRIVATE KEY-----
  • DER的结构

PublicKey

1
2
3
4
RSAPublicKey ::= SEQUENCE {
modulus           INTEGER,  -- n
publicExponent    INTEGER   -- e
}

PrivateKey

1
2
3
4
5
6
7
8
9
10
11
12
RSAPrivateKey ::= SEQUENCE {
version           Version,
modulus           INTEGER,  -- n
publicExponent    INTEGER,  -- e
privateExponent   INTEGER,  -- d
prime1            INTEGER,  -- p
prime2            INTEGER,  -- q
exponent1         INTEGER,  -- d mod (p-1)
exponent2         INTEGER,  -- d mod (q-1)
coefficient       INTEGER,  -- (inverse of q) mod p
otherPrimeInfos   OtherPrimeInfos OPTIONAL
}

PKCS#8

X509,SSL支持的算法不仅仅是RSA,因此产生了更具有通用性的PKCS#8

  • PEM

PublicKey

1
2
3
-----BEGIN PUBLIC KEY-----
BASE64 ENCODED DATA
-----END PUBLIC KEY-----

PrivateKey

1
2
3
-----BEGIN PRIVATE KEY-----
BASE64 ENCODED DATA
-----END PRIVATE KEY-----
  • DER

PublicKey

1
2
3
4
5
6
7
8
9
PublicKeyInfo ::= SEQUENCE {
    algorithm       AlgorithmIdentifier,
    PublicKey       BIT STRING
}

AlgorithmIdentifier ::= SEQUENCE {
    algorithm       OBJECT IDENTIFIER,
    parameters      ANY DEFINED BY algorithm OPTIONAL
}

RSA公钥的OID 为 1.2.840.113549.1.1.1

PrivateKey

1
2
3
4
5
6
7
8
9
10
PrivateKeyInfo ::= SEQUENCE {
    version         Version,
    algorithm       AlgorithmIdentifier,
    PrivateKey      BIT STRING
}

AlgorithmIdentifier ::= SEQUENCE {
    algorithm       OBJECT IDENTIFIER,
    parameters      ANY DEFINED BY algorithm OPTIONAL
}

RSA私钥的OID 为 1.2.840.113549.1.1.1

与PKCS#1相比将文件包含的加密算法和Key分开存储,因此可以存储其他加密算法的Key

SSL

SSL - Secure Sockets Layer,现在应该叫”TLS”,但由于习惯问题,我们还是叫”SSL”比较多.http协议默认情况下是不加密内容的,这样就很可能在内容传播的时候被别人监听到,对于安全性要求较高的场合,必须要加密,https就是带加密的http协议,而https的加密是基于SSL的,它执行的是一个比较下层的加密,也就是说,在加密前,你的服务器程序在干嘛,加密后也一样在干嘛,不用动,这个加密对用户和开发者来说都是透明的.More:[维基百科]

OpenSSL - 简单地说,OpenSSL是SSL的一个实现,SSL只是一种规范.理论上来说,SSL这种规范是安全的,目前的技术水平很难破解,但SSL的实现就可能有些漏洞,如著名的”心脏出血”.OpenSSL还提供了一大堆强大的工具软件,强大到90%我们都用不到.

证书标准

X.509 - 这是一种证书标准,主要定义了证书中应该包含哪些内容.其详情可以参考RFC5280,SSL使用的就是这种证书标准.

相关的文件扩展名

这是比较误导人的地方,虽然我们已经知道有PEM和DER这两种编码格式,但文件扩展名并不一定就叫”PEM”或者”DER”,常见的扩展名除了PEM和DER还有以下这些,它们除了编码格式可能不同之外,内容也有差别,但大多数都能相互转换编码格式.

CRT - CRT应该是certificate的三个字母,其实还是证书的意思,常见于*NIX系统,有可能是PEM编码,也有可能是DER编码,大多数应该是PEM编码,相信你已经知道怎么辨别.

CER - 还是certificate,还是证书,常见于Windows系统,同样的,可能是PEM编码,也可能是DER编码,大多数应该是DER编码.

KEY - 通常用来存放一个公钥或者私钥,并非X.509证书,编码同样的,可能是PEM,也可能是DER. 查看KEY的办法: openssl rsa -in mykey.key -text -noout 如果是DER格式的话,同理应该这样了: openssl rsa -in mykey.key -text -noout -inform der

CSR - Certificate Signing Request,即证书签名请求,这个并不是证书,而是向权威证书颁发机构获得签名证书的申请,其核心内容是一个公钥(当然还附带了一些别的信息),在生成这个申请的时候,同时也会生成一个私钥,私钥要自己保管好.做过iOS APP的朋友都应该知道是怎么向苹果申请开发者证书的吧. 查看的办法: openssl req -noout -text -in my.csr (如果是DER格式的话照旧加上-inform der,这里不写了)

PFX/P12 - predecessor of PKCS#12,对*nix服务器来说,一般CRT和KEY是分开存放在不同文件中的,但Windows的IIS则将它们存在一个PFX文件中,(因此这个文件包含了证书及私钥)这样会不会不安全?应该不会,PFX通常会有一个”提取密码”,你想把里面的东西读取出来的话,它就要求你提供提取密码,PFX使用的时DER编码,如何把PFX转换为PEM编码? openssl pkcs12 -in for-iis.pfx -out for-iis.pem -nodes 这个时候会提示你输入提取代码. for-iis.pem就是可读的文本. 生成pfx的命令类似这样:openssl pkcs12 -export -in certificate.crt -inkey privateKey.key -out certificate.pfx -certfile CACert.crt

其中CACert.crt是CA(权威证书颁发机构)的根证书,有的话也通过-certfile参数一起带进去.这么看来,PFX其实是个证书密钥库.

JKS - 即Java Key Storage,这是Java的专利,跟OpenSSL关系不大,利用Java的一个叫”keytool”的工具,可以将PFX转为JKS,当然了,keytool也能直接生成JKS,不过在此就不多表了.

证书编码的转换

PEM转为DER openssl x509 -in cert.crt -outform der -out cert.der

DER转为PEM openssl x509 -in cert.crt -inform der -outform pem -out cert.pem

(提示:要转换KEY文件也类似,只不过把x509换成rsa,要转CSR的话,把x509换成req…)

使用openssl生成秘钥

生成2048位RSA秘钥,使用3des加密秘钥文件private.pem

openssl genrsa -des3 -out private.pem 2048

导出公钥,默认为PKCS#8结构

openssl rsa -in private.pem -outform PEM -pubout -out public.pem

导出PKCS#1结构的公钥,注意openssl版本,老版本可能不支持

openssl rsa -in private.pem -outform DER -RSAPublicKey_out -out public_pcks1.cer

导出无加密保护的私钥

openssl rsa -in private.pem -out private_unencrypted.pem -outform PEM

参考资料

秘钥格式

ASN.1 key structures in DER and PEM

[那些证书相关的玩意儿(SSL,X.509,PEM,DER,CRT,CER,KEY,CSR,P12等)](https://www.cnblogs.com/guogangj/p/4118605.html)

openssl之PEM系列之1—PEM编码文件结构介绍

Mac OS X 下生成RSA密钥对

| Comments

Mac自带OpenSSL,可以利用openssl来生成RSA密钥对

1.生成私钥,1024指密钥的长度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
test openssl genrsa -out private_key.pem 1024
Generating RSA private key, 1024 bit long modulus
.......................................................................++++++
.......................................................................++++++
e is 65537 (0x10001)
test cat private_key.pem
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDJrT1W3ZyBAYIMNb0XDBQTIGw4TpbrLelQ/K6Yl6abciAUAZNn
j5EHlFhMvpe3iHU0xPIcTM0IM9ZnMjecikHC4lBJ0JmXEsHf4j6Cf2+KRFHTaxPP
hqf65wiGvduTf70xOCvaPeyYVu4x69jm2rtxOeVvF1PJSrV+ZDCoDmYD3QIDAQAB
AoGAToUwmJ13zZJ0u6RAlrSRLFE3UUTn5XDeojV/FNIWf/cTHjbu2SdAZB8Rse+S
ylZKq9zyFqqgOU1VcKBQnpYFu9XFvpoP/xGC/T99MN3chBaQY4wY80FM8NEjWZbP
ZPTczE/HypE+J14/0i0x6jujhFiAAmcE/1ivRzRGvo6qC9ECQQDmcki4jO4VTju7
+WWINatj36pPn/JVUFH8Tqw98tQLg5VMPFfPNsl9L7CSqACCFvl1wiERuxfunRB0
9dNHb4sHAkEA4ApECRgqo+/7Smd2WTa4hSQ+aYs6J7+9e8zjgfIuYBn5ONOsuIuV
VntHyhSc2Xzbp1wkakCzTy/Tyw02kGCs+wJBAMFLHPpHo7AVRf9+yo48zjzgr99H
/yFWVN54MvtnQjtCLKmcd97kSo+Jv+bTqlFz6dy/b7OKpiFMdzBTvdtOkWMCQQC6
21ULUMCfopQv5kLq/ZzATw5O8PQ8Gstq6eQGiXrsZD1cjA9Oi/yt+HxTqwV2z5BT
8aHdjMEAlp9Kh2au3DLpAkB+koitVpjxmWyzQ/Sl4Xa843oR+qneofZZ3m9johtn
+7NQu3JXI1xBckxmZ4DtODtC7MUIqlOEej5OT9QVwljv
-----END RSA PRIVATE KEY-----

2.生成公钥

1
2
3
4
5
6
7
8
9
test openssl rsa -in private_key.pem -pubout -out public_key.pem
writing RSA key
test cat public_key.pem
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJrT1W3ZyBAYIMNb0XDBQTIGw4
TpbrLelQ/K6Yl6abciAUAZNnj5EHlFhMvpe3iHU0xPIcTM0IM9ZnMjecikHC4lBJ
0JmXEsHf4j6Cf2+KRFHTaxPPhqf65wiGvduTf70xOCvaPeyYVu4x69jm2rtxOeVv
F1PJSrV+ZDCoDmYD3QIDAQAB
-----END PUBLIC KEY-----

3.使用pkcs8命令转换私钥格式 PKCS标准

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
test openssl pkcs8 -topk8 -inform PEM -in private_key.pem -outform PEM -nocrypt
-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMmtPVbdnIEBggw1
vRcMFBMgbDhOlust6VD8rpiXpptyIBQBk2ePkQeUWEy+l7eIdTTE8hxMzQgz1mcy
N5yKQcLiUEnQmZcSwd/iPoJ/b4pEUdNrE8+Gp/rnCIa925N/vTE4K9o97JhW7jHr
2Obau3E55W8XU8lKtX5kMKgOZgPdAgMBAAECgYBOhTCYnXfNknS7pECWtJEsUTdR
ROflcN6iNX8U0hZ/9xMeNu7ZJ0BkHxGx75LKVkqr3PIWqqA5TVVwoFCelgW71cW+
mg//EYL9P30w3dyEFpBjjBjzQUzw0SNZls9k9NzMT8fKkT4nXj/SLTHqO6OEWIAC
ZwT/WK9HNEa+jqoL0QJBAOZySLiM7hVOO7v5ZYg1q2Pfqk+f8lVQUfxOrD3y1AuD
lUw8V882yX0vsJKoAIIW+XXCIRG7F+6dEHT100dviwcCQQDgCkQJGCqj7/tKZ3ZZ
NriFJD5pizonv717zOOB8i5gGfk406y4i5VWe0fKFJzZfNunXCRqQLNPL9PLDTaQ
YKz7AkEAwUsc+kejsBVF/37KjjzOPOCv30f/IVZU3ngy+2dCO0IsqZx33uRKj4m/
5tOqUXPp3L9vs4qmIUx3MFO9206RYwJBALrbVQtQwJ+ilC/mQur9nMBPDk7w9Dwa
y2rp5AaJeuxkPVyMD06L/K34fFOrBXbPkFPxod2MwQCWn0qHZq7cMukCQH6SiK1W
mPGZbLND9KXhdrzjehH6qd6h9lneb2OiG2f7s1C7clcjXEFyTGZngO04O0LsxQiq
U4R6Pk5P1BXCWO8=

低版本Xcode调试高版本的iOS系统

| Comments

在使用Xcode作为iOS开发的主IDE的情况下,遇到这种情况最好是AppStore中安装最新版本的Xcode.

下面的方案只是一个临时debug的方案

1.找到不支持的iOS版本,Xcode中可以使用快捷键: Cmd+Shift+2 ,下图表示不支持的版本是 iOS 12.0 (16A5318d)

Could not locate device support files

2.Terminal cd 到Xcode DeviceSupport 目录下

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport

1
2
3
4
5
6
cd /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport
➜  DeviceSupport ls
10.0         10.3         11.2         8.0          8.3          9.1
10.1         11.0         11.3         8.1          8.4          9.2
10.2         11.1         11.4 (15F79) 8.2          9.0          9.3
➜  DeviceSupport
3.Important 去网上搜索真机支持包,可以输出关键字”xcode 12.0 (16A5318d) 真机支持包”,一般情况下百度网盘中都会有,下载下来,copy到2的目录下,然后重启Xcode,搞定
4.不下载真机支持包的方案

直接拷贝一个DeviceSupport现有的支持版本,重命名为 12.0 (16A5318d), 重启Xcode后搞定.
注意这里需要用到root权限,命令前加sudo即可.

1
2
3
4
5
6
7
8
➜  DeviceSupport sudo cp -rf 11.4\ \(15F79\) 12.0\ \(16A5318d\)
Password:
➜  DeviceSupport ls
10.0            11.0            11.4 (15F79)    8.2             9.1
10.1            11.1            12.0 (16A5318d) 8.3             9.2
10.2            11.2            8.0             8.4             9.3
10.3            11.3            8.1             9.0             xinchun
➜  DeviceSupport

success screenshot